blob: 20946f5127e0ca97b8b902fe3e22ae464722bcbf [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/* Must be called with enabled interrupts */
Jeff Garzikd61780c2005-10-30 15:01:51 -0800349/* Ugly. Very ugly. Don't use this for anything else than initialization
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350 code */
351static inline void sx_long_delay(unsigned long delay)
352{
353 unsigned long i;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800354
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355 for (i = jiffies + delay; time_after(i, jiffies); ) ;
356}
357
358
359
360/* Set the IRQ using the RTS lines that run to the PAL on the board.... */
361static int sx_set_irq ( struct specialix_board *bp)
362{
363 int virq;
364 int i;
365 unsigned long flags;
366
Jeff Garzikd61780c2005-10-30 15:01:51 -0800367 if (bp->flags & SX_BOARD_IS_PCI)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 return 1;
369 switch (bp->irq) {
370 /* In the same order as in the docs... */
371 case 15: virq = 0;break;
372 case 12: virq = 1;break;
373 case 11: virq = 2;break;
374 case 9: virq = 3;break;
375 default: printk (KERN_ERR "Speclialix: cannot set irq to %d.\n", bp->irq);
376 return 0;
377 }
378 spin_lock_irqsave(&bp->lock, flags);
379 for (i=0;i<2;i++) {
380 sx_out(bp, CD186x_CAR, i);
381 sx_out(bp, CD186x_MSVRTS, ((virq >> i) & 0x1)? MSVR_RTS:0);
382 }
383 spin_unlock_irqrestore(&bp->lock, flags);
384 return 1;
385}
386
387
388/* Reset and setup CD186x chip */
389static int sx_init_CD186x(struct specialix_board * bp)
390{
391 unsigned long flags;
392 int scaler;
393 int rv = 1;
394
395 func_enter();
396 sx_wait_CCR_off(bp); /* Wait for CCR ready */
397 spin_lock_irqsave(&bp->lock, flags);
398 sx_out_off(bp, CD186x_CCR, CCR_HARDRESET); /* Reset CD186x chip */
399 spin_unlock_irqrestore(&bp->lock, flags);
400 sx_long_delay(HZ/20); /* Delay 0.05 sec */
401 spin_lock_irqsave(&bp->lock, flags);
402 sx_out_off(bp, CD186x_GIVR, SX_ID); /* Set ID for this chip */
403 sx_out_off(bp, CD186x_GICR, 0); /* Clear all bits */
404 sx_out_off(bp, CD186x_PILR1, SX_ACK_MINT); /* Prio for modem intr */
405 sx_out_off(bp, CD186x_PILR2, SX_ACK_TINT); /* Prio for transmitter intr */
406 sx_out_off(bp, CD186x_PILR3, SX_ACK_RINT); /* Prio for receiver intr */
407 /* Set RegAckEn */
408 sx_out_off(bp, CD186x_SRCR, sx_in (bp, CD186x_SRCR) | SRCR_REGACKEN);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800409
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 /* Setting up prescaler. We need 4 ticks per 1 ms */
411 scaler = SX_OSCFREQ/SPECIALIX_TPS;
412
413 sx_out_off(bp, CD186x_PPRH, scaler >> 8);
414 sx_out_off(bp, CD186x_PPRL, scaler & 0xff);
415 spin_unlock_irqrestore(&bp->lock, flags);
416
417 if (!sx_set_irq (bp)) {
418 /* Figure out how to pass this along... */
419 printk (KERN_ERR "Cannot set irq to %d.\n", bp->irq);
420 rv = 0;
421 }
422
423 func_exit();
424 return rv;
425}
426
427
428static int read_cross_byte (struct specialix_board *bp, int reg, int bit)
429{
430 int i;
431 int t;
432 unsigned long flags;
433
434 spin_lock_irqsave(&bp->lock, flags);
435 for (i=0, t=0;i<8;i++) {
436 sx_out_off (bp, CD186x_CAR, i);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800437 if (sx_in_off (bp, reg) & bit)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438 t |= 1 << i;
439 }
440 spin_unlock_irqrestore(&bp->lock, flags);
441
442 return t;
443}
444
445
446#ifdef SPECIALIX_TIMER
447void missed_irq (unsigned long data)
448{
449 unsigned char irq;
450 unsigned long flags;
451 struct specialix_board *bp = (struct specialix_board *)data;
452
453 spin_lock_irqsave(&bp->lock, flags);
454 irq = sx_in ((struct specialix_board *)data, CD186x_SRSR) &
455 (SRSR_RREQint |
456 SRSR_TREQint |
457 SRSR_MREQint);
458 spin_unlock_irqrestore(&bp->lock, flags);
459 if (irq) {
460 printk (KERN_INFO "Missed interrupt... Calling int from timer. \n");
Jeff Garzikd61780c2005-10-30 15:01:51 -0800461 sx_interrupt (((struct specialix_board *)data)->irq,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462 (void*)data, NULL);
463 }
464 missed_irq_timer.expires = jiffies + sx_poll;
465 add_timer (&missed_irq_timer);
466}
467#endif
468
469
470
471/* Main probing routine, also sets irq. */
472static int sx_probe(struct specialix_board *bp)
473{
474 unsigned char val1, val2;
475#if 0
476 int irqs = 0;
477 int retries;
478#endif
479 int rev;
480 int chip;
481
482 func_enter();
483
Jeff Garzikd61780c2005-10-30 15:01:51 -0800484 if (sx_request_io_range(bp)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485 func_exit();
486 return 1;
487 }
488
489 /* Are the I/O ports here ? */
490 sx_out_off(bp, CD186x_PPRL, 0x5a);
491 short_pause ();
492 val1 = sx_in_off(bp, CD186x_PPRL);
493
494 sx_out_off(bp, CD186x_PPRL, 0xa5);
495 short_pause ();
496 val2 = sx_in_off(bp, CD186x_PPRL);
497
Jeff Garzikd61780c2005-10-30 15:01:51 -0800498
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499 if ((val1 != 0x5a) || (val2 != 0xa5)) {
500 printk(KERN_INFO "sx%d: specialix IO8+ Board at 0x%03x not found.\n",
501 board_No(bp), bp->base);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800502 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503 func_exit();
504 return 1;
505 }
506
Jeff Garzikd61780c2005-10-30 15:01:51 -0800507 /* Check the DSR lines that Specialix uses as board
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508 identification */
509 val1 = read_cross_byte (bp, CD186x_MSVR, MSVR_DSR);
510 val2 = read_cross_byte (bp, CD186x_MSVR, MSVR_RTS);
511 dprintk (SX_DEBUG_INIT, "sx%d: DSR lines are: %02x, rts lines are: %02x\n",
512 board_No(bp), val1, val2);
513
514 /* They managed to switch the bit order between the docs and
515 the IO8+ card. The new PCI card now conforms to old docs.
516 They changed the PCI docs to reflect the situation on the
517 old card. */
518 val2 = (bp->flags & SX_BOARD_IS_PCI)?0x4d : 0xb2;
519 if (val1 != val2) {
520 printk(KERN_INFO "sx%d: specialix IO8+ ID %02x at 0x%03x not found (%02x).\n",
521 board_No(bp), val2, bp->base, val1);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800522 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523 func_exit();
524 return 1;
525 }
526
527
528#if 0
529 /* It's time to find IRQ for this board */
530 for (retries = 0; retries < 5 && irqs <= 0; retries++) {
531 irqs = probe_irq_on();
532 sx_init_CD186x(bp); /* Reset CD186x chip */
533 sx_out(bp, CD186x_CAR, 2); /* Select port 2 */
534 sx_wait_CCR(bp);
535 sx_out(bp, CD186x_CCR, CCR_TXEN); /* Enable transmitter */
536 sx_out(bp, CD186x_IER, IER_TXRDY); /* Enable tx empty intr */
Jeff Garzikd61780c2005-10-30 15:01:51 -0800537 sx_long_delay(HZ/20);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 irqs = probe_irq_off(irqs);
539
540 dprintk (SX_DEBUG_INIT, "SRSR = %02x, ", sx_in(bp, CD186x_SRSR));
541 dprintk (SX_DEBUG_INIT, "TRAR = %02x, ", sx_in(bp, CD186x_TRAR));
542 dprintk (SX_DEBUG_INIT, "GIVR = %02x, ", sx_in(bp, CD186x_GIVR));
543 dprintk (SX_DEBUG_INIT, "GICR = %02x, ", sx_in(bp, CD186x_GICR));
544 dprintk (SX_DEBUG_INIT, "\n");
545
546 /* Reset CD186x again */
547 if (!sx_init_CD186x(bp)) {
548 /* Hmmm. This is dead code anyway. */
549 }
550
551 dprintk (SX_DEBUG_INIT "val1 = %02x, val2 = %02x, val3 = %02x.\n",
Jeff Garzikd61780c2005-10-30 15:01:51 -0800552 val1, val2, val3);
553
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800555
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556#if 0
557 if (irqs <= 0) {
558 printk(KERN_ERR "sx%d: Can't find IRQ for specialix IO8+ board at 0x%03x.\n",
559 board_No(bp), bp->base);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800560 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561 func_exit();
562 return 1;
563 }
564#endif
565 printk (KERN_INFO "Started with irq=%d, but now have irq=%d.\n", bp->irq, irqs);
566 if (irqs > 0)
567 bp->irq = irqs;
568#endif
569 /* Reset CD186x again */
570 if (!sx_init_CD186x(bp)) {
Jeff Garzikd61780c2005-10-30 15:01:51 -0800571 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572 func_exit();
Jeff Garzikd61780c2005-10-30 15:01:51 -0800573 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574 }
575
576 sx_request_io_range(bp);
577 bp->flags |= SX_BOARD_PRESENT;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800578
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 /* Chip revcode pkgtype
580 GFRCR SRCR bit 7
581 CD180 rev B 0x81 0
582 CD180 rev C 0x82 0
583 CD1864 rev A 0x82 1
Jeff Garzikd61780c2005-10-30 15:01:51 -0800584 CD1865 rev A 0x83 1 -- Do not use!!! Does not work.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585 CD1865 rev B 0x84 1
586 -- Thanks to Gwen Wang, Cirrus Logic.
587 */
588
589 switch (sx_in_off(bp, CD186x_GFRCR)) {
590 case 0x82:chip = 1864;rev='A';break;
591 case 0x83:chip = 1865;rev='A';break;
592 case 0x84:chip = 1865;rev='B';break;
593 case 0x85:chip = 1865;rev='C';break; /* Does not exist at this time */
594 default:chip=-1;rev='x';
595 }
596
597 dprintk (SX_DEBUG_INIT, " GFCR = 0x%02x\n", sx_in_off(bp, CD186x_GFRCR) );
598
599#ifdef SPECIALIX_TIMER
600 init_timer (&missed_irq_timer);
601 missed_irq_timer.function = missed_irq;
602 missed_irq_timer.data = (unsigned long) bp;
603 missed_irq_timer.expires = jiffies + sx_poll;
604 add_timer (&missed_irq_timer);
605#endif
606
607 printk(KERN_INFO"sx%d: specialix IO8+ board detected at 0x%03x, IRQ %d, CD%d Rev. %c.\n",
608 board_No(bp),
609 bp->base, bp->irq,
610 chip, rev);
611
612 func_exit();
613 return 0;
614}
615
Jeff Garzikd61780c2005-10-30 15:01:51 -0800616/*
617 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 * Interrupt processing routines.
619 * */
620
621static inline void sx_mark_event(struct specialix_port * port, int event)
622{
623 func_enter();
624
625 set_bit(event, &port->event);
626 schedule_work(&port->tqueue);
627
628 func_exit();
629}
630
631
632static inline struct specialix_port * sx_get_port(struct specialix_board * bp,
633 unsigned char const * what)
634{
635 unsigned char channel;
636 struct specialix_port * port = NULL;
637
638 channel = sx_in(bp, CD186x_GICR) >> GICR_CHAN_OFF;
639 dprintk (SX_DEBUG_CHAN, "channel: %d\n", channel);
640 if (channel < CD186x_NCH) {
641 port = &sx_port[board_No(bp) * SX_NPORT + channel];
642 dprintk (SX_DEBUG_CHAN, "port: %d %p flags: 0x%x\n",board_No(bp) * SX_NPORT + channel, port, port->flags & ASYNC_INITIALIZED);
643
644 if (port->flags & ASYNC_INITIALIZED) {
645 dprintk (SX_DEBUG_CHAN, "port: %d %p\n", channel, port);
646 func_exit();
647 return port;
648 }
649 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800650 printk(KERN_INFO "sx%d: %s interrupt from invalid port %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651 board_No(bp), what, channel);
652 return NULL;
653}
654
655
656static inline void sx_receive_exc(struct specialix_board * bp)
657{
658 struct specialix_port *port;
659 struct tty_struct *tty;
660 unsigned char status;
Alan Cox33f0f882006-01-09 20:54:13 -0800661 unsigned char ch, flag;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662
663 func_enter();
664
665 port = sx_get_port(bp, "Receive");
666 if (!port) {
667 dprintk (SX_DEBUG_RX, "Hmm, couldn't find port.\n");
668 func_exit();
669 return;
670 }
671 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800672
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673 status = sx_in(bp, CD186x_RCSR);
674
675 dprintk (SX_DEBUG_RX, "status: 0x%x\n", status);
676 if (status & RCSR_OE) {
677 port->overrun++;
678 dprintk(SX_DEBUG_FIFO, "sx%d: port %d: Overrun. Total %ld overruns.\n",
679 board_No(bp), port_No(port), port->overrun);
680 }
681 status &= port->mark_mask;
682
683 /* This flip buffer check needs to be below the reading of the
684 status register to reset the chip's IRQ.... */
Alan Cox33f0f882006-01-09 20:54:13 -0800685 if (tty_buffer_request_room(tty, 1) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686 dprintk(SX_DEBUG_FIFO, "sx%d: port %d: Working around flip buffer overflow.\n",
687 board_No(bp), port_No(port));
688 func_exit();
689 return;
690 }
691
692 ch = sx_in(bp, CD186x_RDR);
693 if (!status) {
694 func_exit();
695 return;
696 }
697 if (status & RCSR_TOUT) {
Jeff Garzikd61780c2005-10-30 15:01:51 -0800698 printk(KERN_INFO "sx%d: port %d: Receiver timeout. Hardware problems ?\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699 board_No(bp), port_No(port));
700 func_exit();
701 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800702
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 } else if (status & RCSR_BREAK) {
704 dprintk(SX_DEBUG_RX, "sx%d: port %d: Handling break...\n",
705 board_No(bp), port_No(port));
Alan Cox33f0f882006-01-09 20:54:13 -0800706 flag = TTY_BREAK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707 if (port->flags & ASYNC_SAK)
708 do_SAK(tty);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800709
710 } else if (status & RCSR_PE)
Alan Cox33f0f882006-01-09 20:54:13 -0800711 flag = TTY_PARITY;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800712
713 else if (status & RCSR_FE)
Alan Cox33f0f882006-01-09 20:54:13 -0800714 flag = TTY_FRAME;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800715
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716 else if (status & RCSR_OE)
Alan Cox33f0f882006-01-09 20:54:13 -0800717 flag = TTY_OVERRUN;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800718
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719 else
Alan Cox33f0f882006-01-09 20:54:13 -0800720 flag = TTY_NORMAL;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800721
Alan Cox33f0f882006-01-09 20:54:13 -0800722 if(tty_insert_flip_char(tty, ch, flag))
723 tty_flip_buffer_push(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 func_exit();
725}
726
727
728static inline void sx_receive(struct specialix_board * bp)
729{
730 struct specialix_port *port;
731 struct tty_struct *tty;
732 unsigned char count;
733
734 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -0800735
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736 if (!(port = sx_get_port(bp, "Receive"))) {
737 dprintk (SX_DEBUG_RX, "Hmm, couldn't find port.\n");
738 func_exit();
739 return;
740 }
741 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800742
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743 count = sx_in(bp, CD186x_RDCR);
744 dprintk (SX_DEBUG_RX, "port: %p: count: %d\n", port, count);
745 port->hits[count > 8 ? 9 : count]++;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800746
Alan Cox33f0f882006-01-09 20:54:13 -0800747 tty_buffer_request_room(tty, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748
Alan Cox33f0f882006-01-09 20:54:13 -0800749 while (count--)
750 tty_insert_flip_char(tty, sx_in(bp, CD186x_RDR), TTY_NORMAL);
751 tty_flip_buffer_push(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752 func_exit();
753}
754
755
756static inline void sx_transmit(struct specialix_board * bp)
757{
758 struct specialix_port *port;
759 struct tty_struct *tty;
760 unsigned char count;
761
762 func_enter();
763 if (!(port = sx_get_port(bp, "Transmit"))) {
764 func_exit();
765 return;
766 }
767 dprintk (SX_DEBUG_TX, "port: %p\n", port);
768 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800769
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770 if (port->IER & IER_TXEMPTY) {
771 /* FIFO drained */
772 sx_out(bp, CD186x_CAR, port_No(port));
773 port->IER &= ~IER_TXEMPTY;
774 sx_out(bp, CD186x_IER, port->IER);
775 func_exit();
776 return;
777 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800778
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779 if ((port->xmit_cnt <= 0 && !port->break_length)
780 || tty->stopped || tty->hw_stopped) {
781 sx_out(bp, CD186x_CAR, port_No(port));
782 port->IER &= ~IER_TXRDY;
783 sx_out(bp, CD186x_IER, port->IER);
784 func_exit();
785 return;
786 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800787
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 if (port->break_length) {
789 if (port->break_length > 0) {
790 if (port->COR2 & COR2_ETC) {
791 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
792 sx_out(bp, CD186x_TDR, CD186x_C_SBRK);
793 port->COR2 &= ~COR2_ETC;
794 }
795 count = min_t(int, port->break_length, 0xff);
796 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
797 sx_out(bp, CD186x_TDR, CD186x_C_DELAY);
798 sx_out(bp, CD186x_TDR, count);
799 if (!(port->break_length -= count))
800 port->break_length--;
801 } else {
802 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
803 sx_out(bp, CD186x_TDR, CD186x_C_EBRK);
804 sx_out(bp, CD186x_COR2, port->COR2);
805 sx_wait_CCR(bp);
806 sx_out(bp, CD186x_CCR, CCR_CORCHG2);
807 port->break_length = 0;
808 }
809
810 func_exit();
811 return;
812 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800813
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814 count = CD186x_NFIFO;
815 do {
816 sx_out(bp, CD186x_TDR, port->xmit_buf[port->xmit_tail++]);
817 port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1);
818 if (--port->xmit_cnt <= 0)
819 break;
820 } while (--count > 0);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800821
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822 if (port->xmit_cnt <= 0) {
823 sx_out(bp, CD186x_CAR, port_No(port));
824 port->IER &= ~IER_TXRDY;
825 sx_out(bp, CD186x_IER, port->IER);
826 }
827 if (port->xmit_cnt <= port->wakeup_chars)
828 sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);
829
830 func_exit();
831}
832
833
834static inline void sx_check_modem(struct specialix_board * bp)
835{
836 struct specialix_port *port;
837 struct tty_struct *tty;
838 unsigned char mcr;
839 int msvr_cd;
840
841 dprintk (SX_DEBUG_SIGNALS, "Modem intr. ");
842 if (!(port = sx_get_port(bp, "Modem")))
843 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800844
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800846
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847 mcr = sx_in(bp, CD186x_MCR);
848 printk ("mcr = %02x.\n", mcr);
849
850 if ((mcr & MCR_CDCHG)) {
851 dprintk (SX_DEBUG_SIGNALS, "CD just changed... ");
852 msvr_cd = sx_in(bp, CD186x_MSVR) & MSVR_CD;
853 if (msvr_cd) {
854 dprintk (SX_DEBUG_SIGNALS, "Waking up guys in open.\n");
855 wake_up_interruptible(&port->open_wait);
856 } else {
857 dprintk (SX_DEBUG_SIGNALS, "Sending HUP.\n");
858 schedule_work(&port->tqueue_hangup);
859 }
860 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800861
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
863 if (mcr & MCR_CTSCHG) {
864 if (sx_in(bp, CD186x_MSVR) & MSVR_CTS) {
865 tty->hw_stopped = 0;
866 port->IER |= IER_TXRDY;
867 if (port->xmit_cnt <= port->wakeup_chars)
868 sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);
869 } else {
870 tty->hw_stopped = 1;
871 port->IER &= ~IER_TXRDY;
872 }
873 sx_out(bp, CD186x_IER, port->IER);
874 }
875 if (mcr & MCR_DSSXHG) {
876 if (sx_in(bp, CD186x_MSVR) & MSVR_DSR) {
877 tty->hw_stopped = 0;
878 port->IER |= IER_TXRDY;
879 if (port->xmit_cnt <= port->wakeup_chars)
880 sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);
881 } else {
882 tty->hw_stopped = 1;
883 port->IER &= ~IER_TXRDY;
884 }
885 sx_out(bp, CD186x_IER, port->IER);
886 }
887#endif /* SPECIALIX_BRAIN_DAMAGED_CTS */
Jeff Garzikd61780c2005-10-30 15:01:51 -0800888
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889 /* Clear change bits */
890 sx_out(bp, CD186x_MCR, 0);
891}
892
893
894/* The main interrupt processing routine */
David Howells7d12e782006-10-05 14:55:46 +0100895static irqreturn_t sx_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896{
897 unsigned char status;
898 unsigned char ack;
899 struct specialix_board *bp;
900 unsigned long loop = 0;
901 int saved_reg;
902 unsigned long flags;
903
904 func_enter();
905
906 bp = dev_id;
907 spin_lock_irqsave(&bp->lock, flags);
908
909 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 -0400910 if (!(bp->flags & SX_BOARD_ACTIVE)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911 dprintk (SX_DEBUG_IRQ, "sx: False interrupt. irq %d.\n", irq);
912 spin_unlock_irqrestore(&bp->lock, flags);
913 func_exit();
914 return IRQ_NONE;
915 }
916
917 saved_reg = bp->reg;
918
919 while ((++loop < 16) && (status = (sx_in(bp, CD186x_SRSR) &
920 (SRSR_RREQint |
921 SRSR_TREQint |
Jeff Garzikd61780c2005-10-30 15:01:51 -0800922 SRSR_MREQint)))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923 if (status & SRSR_RREQint) {
924 ack = sx_in(bp, CD186x_RRAR);
925
926 if (ack == (SX_ID | GIVR_IT_RCV))
927 sx_receive(bp);
928 else if (ack == (SX_ID | GIVR_IT_REXC))
929 sx_receive_exc(bp);
930 else
931 printk(KERN_ERR "sx%d: status: 0x%x Bad receive ack 0x%02x.\n",
932 board_No(bp), status, ack);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800933
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934 } else if (status & SRSR_TREQint) {
935 ack = sx_in(bp, CD186x_TRAR);
936
937 if (ack == (SX_ID | GIVR_IT_TX))
938 sx_transmit(bp);
939 else
940 printk(KERN_ERR "sx%d: status: 0x%x Bad transmit ack 0x%02x. port: %d\n",
941 board_No(bp), status, ack, port_No (sx_get_port (bp, "Int")));
942 } else if (status & SRSR_MREQint) {
943 ack = sx_in(bp, CD186x_MRAR);
944
Jeff Garzikd61780c2005-10-30 15:01:51 -0800945 if (ack == (SX_ID | GIVR_IT_MODEM))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946 sx_check_modem(bp);
947 else
948 printk(KERN_ERR "sx%d: status: 0x%x Bad modem ack 0x%02x.\n",
949 board_No(bp), status, ack);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800950
951 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700952
953 sx_out(bp, CD186x_EOIR, 0); /* Mark end of interrupt */
954 }
955 bp->reg = saved_reg;
956 outb (bp->reg, bp->base + SX_ADDR_REG);
957 spin_unlock_irqrestore(&bp->lock, flags);
958 func_exit();
959 return IRQ_HANDLED;
960}
961
962
963/*
964 * Routines for open & close processing.
965 */
966
967static void turn_ints_off (struct specialix_board *bp)
968{
969 unsigned long flags;
970
971 func_enter();
972 if (bp->flags & SX_BOARD_IS_PCI) {
973 /* This was intended for enabeling the interrupt on the
974 * PCI card. However it seems that it's already enabled
975 * and as PCI interrupts can be shared, there is no real
976 * reason to have to turn it off. */
977 }
978
979 spin_lock_irqsave(&bp->lock, flags);
980 (void) sx_in_off (bp, 0); /* Turn off interrupts. */
981 spin_unlock_irqrestore(&bp->lock, flags);
982
983 func_exit();
984}
985
986static void turn_ints_on (struct specialix_board *bp)
987{
988 unsigned long flags;
989
990 func_enter();
991
992 if (bp->flags & SX_BOARD_IS_PCI) {
993 /* play with the PCI chip. See comment above. */
994 }
995 spin_lock_irqsave(&bp->lock, flags);
996 (void) sx_in (bp, 0); /* Turn ON interrupts. */
997 spin_unlock_irqrestore(&bp->lock, flags);
998
999 func_exit();
1000}
1001
1002
1003/* Called with disabled interrupts */
1004static inline int sx_setup_board(struct specialix_board * bp)
1005{
1006 int error;
1007
Jeff Garzikd61780c2005-10-30 15:01:51 -08001008 if (bp->flags & SX_BOARD_ACTIVE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001009 return 0;
1010
1011 if (bp->flags & SX_BOARD_IS_PCI)
Thomas Gleixner0f2ed4c2006-07-01 19:29:33 -07001012 error = request_irq(bp->irq, sx_interrupt, IRQF_DISABLED | IRQF_SHARED, "specialix IO8+", bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013 else
Thomas Gleixner0f2ed4c2006-07-01 19:29:33 -07001014 error = request_irq(bp->irq, sx_interrupt, IRQF_DISABLED, "specialix IO8+", bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015
Jeff Garzikd61780c2005-10-30 15:01:51 -08001016 if (error)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017 return error;
1018
1019 turn_ints_on (bp);
1020 bp->flags |= SX_BOARD_ACTIVE;
1021
1022 return 0;
1023}
1024
1025
1026/* Called with disabled interrupts */
1027static inline void sx_shutdown_board(struct specialix_board *bp)
1028{
1029 func_enter();
1030
1031 if (!(bp->flags & SX_BOARD_ACTIVE)) {
1032 func_exit();
1033 return;
1034 }
1035
1036 bp->flags &= ~SX_BOARD_ACTIVE;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001037
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038 dprintk (SX_DEBUG_IRQ, "Freeing IRQ%d for board %d.\n",
1039 bp->irq, board_No (bp));
1040 free_irq(bp->irq, bp);
1041
1042 turn_ints_off (bp);
1043
1044
1045 func_exit();
1046}
1047
1048
1049/*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001050 * Setting up port characteristics.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051 * Must be called with disabled interrupts
1052 */
1053static void sx_change_speed(struct specialix_board *bp, struct specialix_port *port)
1054{
1055 struct tty_struct *tty;
1056 unsigned long baud;
1057 long tmp;
1058 unsigned char cor1 = 0, cor3 = 0;
1059 unsigned char mcor1 = 0, mcor2 = 0;
1060 static unsigned long again;
1061 unsigned long flags;
1062
1063 func_enter();
1064
1065 if (!(tty = port->tty) || !tty->termios) {
1066 func_exit();
1067 return;
1068 }
1069
1070 port->IER = 0;
1071 port->COR2 = 0;
1072 /* Select port on the board */
1073 spin_lock_irqsave(&bp->lock, flags);
1074 sx_out(bp, CD186x_CAR, port_No(port));
1075
1076 /* The Specialix board doens't implement the RTS lines.
1077 They are used to set the IRQ level. Don't touch them. */
1078 if (SX_CRTSCTS(tty))
1079 port->MSVR = MSVR_DTR | (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
1080 else
1081 port->MSVR = (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
1082 spin_unlock_irqrestore(&bp->lock, flags);
1083 dprintk (SX_DEBUG_TERMIOS, "sx: got MSVR=%02x.\n", port->MSVR);
Alan Cox67cc0162006-09-29 02:01:39 -07001084 baud = tty_get_baud_rate(tty);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001085
Alan Cox67cc0162006-09-29 02:01:39 -07001086 if (baud == 38400) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087 if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
Adrian Bunka4bb2cf2006-10-17 00:10:00 -07001088 baud = 57600;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089 if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
Adrian Bunka4bb2cf2006-10-17 00:10:00 -07001090 baud = 115200;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001092
Alan Cox67cc0162006-09-29 02:01:39 -07001093 if (!baud) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001094 /* Drop DTR & exit */
1095 dprintk (SX_DEBUG_TERMIOS, "Dropping DTR... Hmm....\n");
1096 if (!SX_CRTSCTS (tty)) {
1097 port -> MSVR &= ~ MSVR_DTR;
1098 spin_lock_irqsave(&bp->lock, flags);
1099 sx_out(bp, CD186x_MSVR, port->MSVR );
1100 spin_unlock_irqrestore(&bp->lock, flags);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001101 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001102 else
1103 dprintk (SX_DEBUG_TERMIOS, "Can't drop DTR: no DTR.\n");
1104 return;
1105 } else {
1106 /* Set DTR on */
1107 if (!SX_CRTSCTS (tty)) {
1108 port ->MSVR |= MSVR_DTR;
1109 }
1110 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001111
Linus Torvalds1da177e2005-04-16 15:20:36 -07001112 /*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001113 * Now we must calculate some speed depended things
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114 */
1115
1116 /* Set baud rate for port */
1117 tmp = port->custom_divisor ;
1118 if ( tmp )
1119 printk (KERN_INFO "sx%d: Using custom baud rate divisor %ld. \n"
1120 "This is an untested option, please be carefull.\n",
1121 port_No (port), tmp);
1122 else
Alan Cox67cc0162006-09-29 02:01:39 -07001123 tmp = (((SX_OSCFREQ + baud/2) / baud +
Linus Torvalds1da177e2005-04-16 15:20:36 -07001124 CD186x_TPC/2) / CD186x_TPC);
1125
Jeff Garzikd61780c2005-10-30 15:01:51 -08001126 if ((tmp < 0x10) && time_before(again, jiffies)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001127 again = jiffies + HZ * 60;
1128 /* Page 48 of version 2.0 of the CL-CD1865 databook */
1129 if (tmp >= 12) {
1130 printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
1131 "Performance degradation is possible.\n"
1132 "Read specialix.txt for more info.\n",
1133 port_No (port), tmp);
1134 } else {
1135 printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
1136 "Warning: overstressing Cirrus chip. "
1137 "This might not work.\n"
Jeff Garzikd61780c2005-10-30 15:01:51 -08001138 "Read specialix.txt for more info.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139 port_No (port), tmp);
1140 }
1141 }
1142 spin_lock_irqsave(&bp->lock, flags);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001143 sx_out(bp, CD186x_RBPRH, (tmp >> 8) & 0xff);
1144 sx_out(bp, CD186x_TBPRH, (tmp >> 8) & 0xff);
1145 sx_out(bp, CD186x_RBPRL, tmp & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001146 sx_out(bp, CD186x_TBPRL, tmp & 0xff);
1147 spin_unlock_irqrestore(&bp->lock, flags);
Adrian Bunka4bb2cf2006-10-17 00:10:00 -07001148 if (port->custom_divisor)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 baud = (SX_OSCFREQ + port->custom_divisor/2) / port->custom_divisor;
Adrian Bunka4bb2cf2006-10-17 00:10:00 -07001150 baud = (baud + 5) / 10; /* Estimated CPS */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151
1152 /* Two timer ticks seems enough to wakeup something like SLIP driver */
Jeff Garzikd61780c2005-10-30 15:01:51 -08001153 tmp = ((baud + HZ/2) / HZ) * 2 - CD186x_NFIFO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ?
1155 SERIAL_XMIT_SIZE - 1 : tmp);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001156
Linus Torvalds1da177e2005-04-16 15:20:36 -07001157 /* Receiver timeout will be transmission time for 1.5 chars */
1158 tmp = (SPECIALIX_TPS + SPECIALIX_TPS/2 + baud/2) / baud;
1159 tmp = (tmp > 0xff) ? 0xff : tmp;
1160 spin_lock_irqsave(&bp->lock, flags);
1161 sx_out(bp, CD186x_RTPR, tmp);
1162 spin_unlock_irqrestore(&bp->lock, flags);
1163 switch (C_CSIZE(tty)) {
1164 case CS5:
1165 cor1 |= COR1_5BITS;
1166 break;
1167 case CS6:
1168 cor1 |= COR1_6BITS;
1169 break;
1170 case CS7:
1171 cor1 |= COR1_7BITS;
1172 break;
1173 case CS8:
1174 cor1 |= COR1_8BITS;
1175 break;
1176 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001177
1178 if (C_CSTOPB(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001179 cor1 |= COR1_2SB;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001180
Linus Torvalds1da177e2005-04-16 15:20:36 -07001181 cor1 |= COR1_IGNORE;
1182 if (C_PARENB(tty)) {
1183 cor1 |= COR1_NORMPAR;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001184 if (C_PARODD(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001185 cor1 |= COR1_ODDP;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001186 if (I_INPCK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187 cor1 &= ~COR1_IGNORE;
1188 }
1189 /* Set marking of some errors */
1190 port->mark_mask = RCSR_OE | RCSR_TOUT;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001191 if (I_INPCK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001192 port->mark_mask |= RCSR_FE | RCSR_PE;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001193 if (I_BRKINT(tty) || I_PARMRK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001194 port->mark_mask |= RCSR_BREAK;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001195 if (I_IGNPAR(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196 port->mark_mask &= ~(RCSR_FE | RCSR_PE);
1197 if (I_IGNBRK(tty)) {
1198 port->mark_mask &= ~RCSR_BREAK;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001199 if (I_IGNPAR(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001200 /* Real raw mode. Ignore all */
1201 port->mark_mask &= ~RCSR_OE;
1202 }
1203 /* Enable Hardware Flow Control */
1204 if (C_CRTSCTS(tty)) {
1205#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
1206 port->IER |= IER_DSR | IER_CTS;
1207 mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD;
1208 mcor2 |= MCOR2_DSROD | MCOR2_CTSOD;
1209 spin_lock_irqsave(&bp->lock, flags);
1210 tty->hw_stopped = !(sx_in(bp, CD186x_MSVR) & (MSVR_CTS|MSVR_DSR));
1211 spin_unlock_irqrestore(&bp->lock, flags);
1212#else
Jeff Garzikd61780c2005-10-30 15:01:51 -08001213 port->COR2 |= COR2_CTSAE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214#endif
1215 }
1216 /* Enable Software Flow Control. FIXME: I'm not sure about this */
1217 /* Some people reported that it works, but I still doubt it */
1218 if (I_IXON(tty)) {
1219 port->COR2 |= COR2_TXIBE;
1220 cor3 |= (COR3_FCT | COR3_SCDE);
1221 if (I_IXANY(tty))
1222 port->COR2 |= COR2_IXM;
1223 spin_lock_irqsave(&bp->lock, flags);
1224 sx_out(bp, CD186x_SCHR1, START_CHAR(tty));
1225 sx_out(bp, CD186x_SCHR2, STOP_CHAR(tty));
1226 sx_out(bp, CD186x_SCHR3, START_CHAR(tty));
1227 sx_out(bp, CD186x_SCHR4, STOP_CHAR(tty));
1228 spin_unlock_irqrestore(&bp->lock, flags);
1229 }
1230 if (!C_CLOCAL(tty)) {
1231 /* Enable CD check */
1232 port->IER |= IER_CD;
1233 mcor1 |= MCOR1_CDZD;
1234 mcor2 |= MCOR2_CDOD;
1235 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001236
1237 if (C_CREAD(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001238 /* Enable receiver */
1239 port->IER |= IER_RXD;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001240
Linus Torvalds1da177e2005-04-16 15:20:36 -07001241 /* Set input FIFO size (1-8 bytes) */
1242 cor3 |= sx_rxfifo;
1243 /* Setting up CD186x channel registers */
1244 spin_lock_irqsave(&bp->lock, flags);
1245 sx_out(bp, CD186x_COR1, cor1);
1246 sx_out(bp, CD186x_COR2, port->COR2);
1247 sx_out(bp, CD186x_COR3, cor3);
1248 spin_unlock_irqrestore(&bp->lock, flags);
1249 /* Make CD186x know about registers change */
1250 sx_wait_CCR(bp);
1251 spin_lock_irqsave(&bp->lock, flags);
1252 sx_out(bp, CD186x_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3);
1253 /* Setting up modem option registers */
1254 dprintk (SX_DEBUG_TERMIOS, "Mcor1 = %02x, mcor2 = %02x.\n", mcor1, mcor2);
1255 sx_out(bp, CD186x_MCOR1, mcor1);
1256 sx_out(bp, CD186x_MCOR2, mcor2);
1257 spin_unlock_irqrestore(&bp->lock, flags);
1258 /* Enable CD186x transmitter & receiver */
1259 sx_wait_CCR(bp);
1260 spin_lock_irqsave(&bp->lock, flags);
1261 sx_out(bp, CD186x_CCR, CCR_TXEN | CCR_RXEN);
1262 /* Enable interrupts */
1263 sx_out(bp, CD186x_IER, port->IER);
1264 /* And finally set the modem lines... */
1265 sx_out(bp, CD186x_MSVR, port->MSVR);
1266 spin_unlock_irqrestore(&bp->lock, flags);
1267
1268 func_exit();
1269}
1270
1271
1272/* Must be called with interrupts enabled */
1273static int sx_setup_port(struct specialix_board *bp, struct specialix_port *port)
1274{
1275 unsigned long flags;
1276
1277 func_enter();
1278
1279 if (port->flags & ASYNC_INITIALIZED) {
1280 func_exit();
1281 return 0;
1282 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001283
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284 if (!port->xmit_buf) {
1285 /* We may sleep in get_zeroed_page() */
1286 unsigned long tmp;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001287
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288 if (!(tmp = get_zeroed_page(GFP_KERNEL))) {
1289 func_exit();
1290 return -ENOMEM;
1291 }
1292
1293 if (port->xmit_buf) {
1294 free_page(tmp);
1295 func_exit();
1296 return -ERESTARTSYS;
1297 }
1298 port->xmit_buf = (unsigned char *) tmp;
1299 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001300
Linus Torvalds1da177e2005-04-16 15:20:36 -07001301 spin_lock_irqsave(&port->lock, flags);
1302
Jeff Garzikd61780c2005-10-30 15:01:51 -08001303 if (port->tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001304 clear_bit(TTY_IO_ERROR, &port->tty->flags);
1305
1306 port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
1307 sx_change_speed(bp, port);
1308 port->flags |= ASYNC_INITIALIZED;
1309
1310 spin_unlock_irqrestore(&port->lock, flags);
1311
Jeff Garzikd61780c2005-10-30 15:01:51 -08001312
Linus Torvalds1da177e2005-04-16 15:20:36 -07001313 func_exit();
1314 return 0;
1315}
1316
1317
1318/* Must be called with interrupts disabled */
1319static void sx_shutdown_port(struct specialix_board *bp, struct specialix_port *port)
1320{
1321 struct tty_struct *tty;
1322 int i;
1323 unsigned long flags;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001324
Linus Torvalds1da177e2005-04-16 15:20:36 -07001325 func_enter();
1326
1327 if (!(port->flags & ASYNC_INITIALIZED)) {
1328 func_exit();
1329 return;
1330 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001331
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332 if (sx_debug & SX_DEBUG_FIFO) {
1333 dprintk(SX_DEBUG_FIFO, "sx%d: port %d: %ld overruns, FIFO hits [ ",
1334 board_No(bp), port_No(port), port->overrun);
1335 for (i = 0; i < 10; i++) {
1336 dprintk(SX_DEBUG_FIFO, "%ld ", port->hits[i]);
1337 }
1338 dprintk(SX_DEBUG_FIFO, "].\n");
1339 }
1340
1341 if (port->xmit_buf) {
1342 free_page((unsigned long) port->xmit_buf);
1343 port->xmit_buf = NULL;
1344 }
1345
1346 /* Select port */
1347 spin_lock_irqsave(&bp->lock, flags);
1348 sx_out(bp, CD186x_CAR, port_No(port));
1349
1350 if (!(tty = port->tty) || C_HUPCL(tty)) {
1351 /* Drop DTR */
1352 sx_out(bp, CD186x_MSVDTR, 0);
1353 }
1354 spin_unlock_irqrestore(&bp->lock, flags);
1355 /* Reset port */
1356 sx_wait_CCR(bp);
1357 spin_lock_irqsave(&bp->lock, flags);
1358 sx_out(bp, CD186x_CCR, CCR_SOFTRESET);
1359 /* Disable all interrupts from this port */
1360 port->IER = 0;
1361 sx_out(bp, CD186x_IER, port->IER);
1362 spin_unlock_irqrestore(&bp->lock, flags);
1363 if (tty)
1364 set_bit(TTY_IO_ERROR, &tty->flags);
1365 port->flags &= ~ASYNC_INITIALIZED;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001366
1367 if (!bp->count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368 sx_shutdown_board(bp);
1369 func_exit();
1370}
1371
Jeff Garzikd61780c2005-10-30 15:01:51 -08001372
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373static int block_til_ready(struct tty_struct *tty, struct file * filp,
1374 struct specialix_port *port)
1375{
1376 DECLARE_WAITQUEUE(wait, current);
1377 struct specialix_board *bp = port_Board(port);
1378 int retval;
1379 int do_clocal = 0;
1380 int CD;
1381 unsigned long flags;
1382
1383 func_enter();
1384
1385 /*
1386 * If the device is in the middle of being closed, then block
1387 * until it's done, and then try again.
1388 */
1389 if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {
1390 interruptible_sleep_on(&port->close_wait);
1391 if (port->flags & ASYNC_HUP_NOTIFY) {
1392 func_exit();
1393 return -EAGAIN;
1394 } else {
1395 func_exit();
1396 return -ERESTARTSYS;
1397 }
1398 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001399
Linus Torvalds1da177e2005-04-16 15:20:36 -07001400 /*
1401 * If non-blocking mode is set, or the port is not enabled,
1402 * then make the check up front and then exit.
1403 */
1404 if ((filp->f_flags & O_NONBLOCK) ||
1405 (tty->flags & (1 << TTY_IO_ERROR))) {
1406 port->flags |= ASYNC_NORMAL_ACTIVE;
1407 func_exit();
1408 return 0;
1409 }
1410
1411 if (C_CLOCAL(tty))
1412 do_clocal = 1;
1413
1414 /*
1415 * Block waiting for the carrier detect and the line to become
1416 * free (i.e., not in use by the callout). While we are in
1417 * this loop, info->count is dropped by one, so that
1418 * rs_close() knows when to free things. We restore it upon
1419 * exit, either normal or abnormal.
1420 */
1421 retval = 0;
1422 add_wait_queue(&port->open_wait, &wait);
1423 spin_lock_irqsave(&port->lock, flags);
1424 if (!tty_hung_up_p(filp)) {
1425 port->count--;
1426 }
1427 spin_unlock_irqrestore(&port->lock, flags);
1428 port->blocked_open++;
1429 while (1) {
1430 spin_lock_irqsave(&bp->lock, flags);
1431 sx_out(bp, CD186x_CAR, port_No(port));
1432 CD = sx_in(bp, CD186x_MSVR) & MSVR_CD;
1433 if (SX_CRTSCTS (tty)) {
1434 /* Activate RTS */
1435 port->MSVR |= MSVR_DTR; /* WTF? */
1436 sx_out (bp, CD186x_MSVR, port->MSVR);
1437 } else {
1438 /* Activate DTR */
1439 port->MSVR |= MSVR_DTR;
1440 sx_out (bp, CD186x_MSVR, port->MSVR);
1441 }
1442 spin_unlock_irqrestore(&bp->lock, flags);
1443 set_current_state(TASK_INTERRUPTIBLE);
1444 if (tty_hung_up_p(filp) ||
1445 !(port->flags & ASYNC_INITIALIZED)) {
1446 if (port->flags & ASYNC_HUP_NOTIFY)
1447 retval = -EAGAIN;
1448 else
Jeff Garzikd61780c2005-10-30 15:01:51 -08001449 retval = -ERESTARTSYS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001450 break;
1451 }
1452 if (!(port->flags & ASYNC_CLOSING) &&
1453 (do_clocal || CD))
1454 break;
1455 if (signal_pending(current)) {
1456 retval = -ERESTARTSYS;
1457 break;
1458 }
1459 schedule();
1460 }
1461
1462 set_current_state(TASK_RUNNING);
1463 remove_wait_queue(&port->open_wait, &wait);
1464 spin_lock_irqsave(&port->lock, flags);
1465 if (!tty_hung_up_p(filp)) {
1466 port->count++;
1467 }
1468 port->blocked_open--;
1469 spin_unlock_irqrestore(&port->lock, flags);
1470 if (retval) {
1471 func_exit();
1472 return retval;
1473 }
1474
1475 port->flags |= ASYNC_NORMAL_ACTIVE;
1476 func_exit();
1477 return 0;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001478}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001479
1480
1481static int sx_open(struct tty_struct * tty, struct file * filp)
1482{
1483 int board;
1484 int error;
1485 struct specialix_port * port;
1486 struct specialix_board * bp;
1487 int i;
1488 unsigned long flags;
1489
1490 func_enter();
1491
1492 board = SX_BOARD(tty->index);
1493
1494 if (board >= SX_NBOARD || !(sx_board[board].flags & SX_BOARD_PRESENT)) {
1495 func_exit();
1496 return -ENODEV;
1497 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001498
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499 bp = &sx_board[board];
1500 port = sx_port + board * SX_NPORT + SX_PORT(tty->index);
1501 port->overrun = 0;
1502 for (i = 0; i < 10; i++)
1503 port->hits[i]=0;
1504
1505 dprintk (SX_DEBUG_OPEN, "Board = %d, bp = %p, port = %p, portno = %d.\n",
1506 board, bp, port, SX_PORT(tty->index));
1507
1508 if (sx_paranoia_check(port, tty->name, "sx_open")) {
1509 func_enter();
1510 return -ENODEV;
1511 }
1512
1513 if ((error = sx_setup_board(bp))) {
1514 func_exit();
1515 return error;
1516 }
1517
1518 spin_lock_irqsave(&bp->lock, flags);
1519 port->count++;
1520 bp->count++;
1521 tty->driver_data = port;
1522 port->tty = tty;
1523 spin_unlock_irqrestore(&bp->lock, flags);
1524
1525 if ((error = sx_setup_port(bp, port))) {
1526 func_enter();
1527 return error;
1528 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001529
Linus Torvalds1da177e2005-04-16 15:20:36 -07001530 if ((error = block_til_ready(tty, filp, port))) {
1531 func_enter();
1532 return error;
1533 }
1534
1535 func_exit();
1536 return 0;
1537}
1538
1539
1540static void sx_close(struct tty_struct * tty, struct file * filp)
1541{
1542 struct specialix_port *port = (struct specialix_port *) tty->driver_data;
1543 struct specialix_board *bp;
1544 unsigned long flags;
1545 unsigned long timeout;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001546
Linus Torvalds1da177e2005-04-16 15:20:36 -07001547 func_enter();
1548 if (!port || sx_paranoia_check(port, tty->name, "close")) {
1549 func_exit();
1550 return;
1551 }
1552 spin_lock_irqsave(&port->lock, flags);
1553
1554 if (tty_hung_up_p(filp)) {
1555 spin_unlock_irqrestore(&port->lock, flags);
1556 func_exit();
1557 return;
1558 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001559
Linus Torvalds1da177e2005-04-16 15:20:36 -07001560 bp = port_Board(port);
1561 if ((tty->count == 1) && (port->count != 1)) {
1562 printk(KERN_ERR "sx%d: sx_close: bad port count;"
1563 " tty->count is 1, port count is %d\n",
1564 board_No(bp), port->count);
1565 port->count = 1;
1566 }
1567
1568 if (port->count > 1) {
1569 port->count--;
1570 bp->count--;
1571
1572 spin_unlock_irqrestore(&port->lock, flags);
1573
1574 func_exit();
1575 return;
1576 }
1577 port->flags |= ASYNC_CLOSING;
1578 /*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001579 * Now we wait for the transmit buffer to clear; and we notify
Linus Torvalds1da177e2005-04-16 15:20:36 -07001580 * the line discipline to only process XON/XOFF characters.
1581 */
1582 tty->closing = 1;
1583 spin_unlock_irqrestore(&port->lock, flags);
1584 dprintk (SX_DEBUG_OPEN, "Closing\n");
1585 if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) {
1586 tty_wait_until_sent(tty, port->closing_wait);
1587 }
1588 /*
1589 * At this point we stop accepting input. To do this, we
1590 * disable the receive line status interrupts, and tell the
1591 * interrupt driver to stop checking the data ready bit in the
1592 * line status register.
1593 */
1594 dprintk (SX_DEBUG_OPEN, "Closed\n");
1595 port->IER &= ~IER_RXD;
1596 if (port->flags & ASYNC_INITIALIZED) {
1597 port->IER &= ~IER_TXRDY;
1598 port->IER |= IER_TXEMPTY;
1599 spin_lock_irqsave(&bp->lock, flags);
1600 sx_out(bp, CD186x_CAR, port_No(port));
1601 sx_out(bp, CD186x_IER, port->IER);
1602 spin_unlock_irqrestore(&bp->lock, flags);
1603 /*
1604 * Before we drop DTR, make sure the UART transmitter
1605 * has completely drained; this is especially
1606 * important if there is a transmit FIFO!
1607 */
1608 timeout = jiffies+HZ;
1609 while(port->IER & IER_TXEMPTY) {
1610 set_current_state (TASK_INTERRUPTIBLE);
1611 msleep_interruptible(jiffies_to_msecs(port->timeout));
1612 if (time_after(jiffies, timeout)) {
1613 printk (KERN_INFO "Timeout waiting for close\n");
1614 break;
1615 }
1616 }
1617
1618 }
1619
1620 if (--bp->count < 0) {
1621 printk(KERN_ERR "sx%d: sx_shutdown_port: bad board count: %d port: %d\n",
1622 board_No(bp), bp->count, tty->index);
1623 bp->count = 0;
1624 }
1625 if (--port->count < 0) {
1626 printk(KERN_ERR "sx%d: sx_close: bad port count for tty%d: %d\n",
1627 board_No(bp), port_No(port), port->count);
1628 port->count = 0;
1629 }
1630
1631 sx_shutdown_port(bp, port);
1632 if (tty->driver->flush_buffer)
1633 tty->driver->flush_buffer(tty);
1634 tty_ldisc_flush(tty);
1635 spin_lock_irqsave(&port->lock, flags);
1636 tty->closing = 0;
1637 port->event = 0;
1638 port->tty = NULL;
1639 spin_unlock_irqrestore(&port->lock, flags);
1640 if (port->blocked_open) {
1641 if (port->close_delay) {
1642 msleep_interruptible(jiffies_to_msecs(port->close_delay));
1643 }
1644 wake_up_interruptible(&port->open_wait);
1645 }
1646 port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
1647 wake_up_interruptible(&port->close_wait);
1648
1649 func_exit();
1650}
1651
1652
Jeff Garzikd61780c2005-10-30 15:01:51 -08001653static int sx_write(struct tty_struct * tty,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001654 const unsigned char *buf, int count)
1655{
1656 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1657 struct specialix_board *bp;
1658 int c, total = 0;
1659 unsigned long flags;
1660
1661 func_enter();
1662 if (sx_paranoia_check(port, tty->name, "sx_write")) {
1663 func_exit();
1664 return 0;
1665 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001666
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667 bp = port_Board(port);
1668
Jiri Slaby365e0222006-09-30 23:28:11 -07001669 if (!port->xmit_buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001670 func_exit();
1671 return 0;
1672 }
1673
1674 while (1) {
1675 spin_lock_irqsave(&port->lock, flags);
1676 c = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
1677 SERIAL_XMIT_SIZE - port->xmit_head));
1678 if (c <= 0) {
1679 spin_unlock_irqrestore(&port->lock, flags);
1680 break;
1681 }
1682 memcpy(port->xmit_buf + port->xmit_head, buf, c);
1683 port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
1684 port->xmit_cnt += c;
1685 spin_unlock_irqrestore(&port->lock, flags);
1686
1687 buf += c;
1688 count -= c;
1689 total += c;
1690 }
1691
1692 spin_lock_irqsave(&bp->lock, flags);
1693 if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
1694 !(port->IER & IER_TXRDY)) {
1695 port->IER |= IER_TXRDY;
1696 sx_out(bp, CD186x_CAR, port_No(port));
1697 sx_out(bp, CD186x_IER, port->IER);
1698 }
1699 spin_unlock_irqrestore(&bp->lock, flags);
1700 func_exit();
1701
1702 return total;
1703}
1704
1705
1706static void sx_put_char(struct tty_struct * tty, unsigned char ch)
1707{
1708 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1709 unsigned long flags;
1710 struct specialix_board * bp;
1711
1712 func_enter();
1713
1714 if (sx_paranoia_check(port, tty->name, "sx_put_char")) {
1715 func_exit();
1716 return;
1717 }
1718 dprintk (SX_DEBUG_TX, "check tty: %p %p\n", tty, port->xmit_buf);
Eric Sesterhenn326f28e92006-06-25 05:48:48 -07001719 if (!port->xmit_buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001720 func_exit();
1721 return;
1722 }
1723 bp = port_Board(port);
1724 spin_lock_irqsave(&port->lock, flags);
1725
1726 dprintk (SX_DEBUG_TX, "xmit_cnt: %d xmit_buf: %p\n", port->xmit_cnt, port->xmit_buf);
1727 if ((port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) || (!port->xmit_buf)) {
1728 spin_unlock_irqrestore(&port->lock, flags);
1729 dprintk (SX_DEBUG_TX, "Exit size\n");
1730 func_exit();
1731 return;
1732 }
1733 dprintk (SX_DEBUG_TX, "Handle xmit: %p %p\n", port, port->xmit_buf);
1734 port->xmit_buf[port->xmit_head++] = ch;
1735 port->xmit_head &= SERIAL_XMIT_SIZE - 1;
1736 port->xmit_cnt++;
1737 spin_unlock_irqrestore(&port->lock, flags);
1738
1739 func_exit();
1740}
1741
1742
1743static void sx_flush_chars(struct tty_struct * tty)
1744{
1745 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1746 unsigned long flags;
1747 struct specialix_board * bp = port_Board(port);
1748
1749 func_enter();
1750
1751 if (sx_paranoia_check(port, tty->name, "sx_flush_chars")) {
1752 func_exit();
1753 return;
1754 }
1755 if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
1756 !port->xmit_buf) {
1757 func_exit();
1758 return;
1759 }
1760 spin_lock_irqsave(&bp->lock, flags);
1761 port->IER |= IER_TXRDY;
1762 sx_out(port_Board(port), CD186x_CAR, port_No(port));
1763 sx_out(port_Board(port), CD186x_IER, port->IER);
1764 spin_unlock_irqrestore(&bp->lock, flags);
1765
1766 func_exit();
1767}
1768
1769
1770static int sx_write_room(struct tty_struct * tty)
1771{
1772 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1773 int ret;
1774
1775 func_enter();
1776
1777 if (sx_paranoia_check(port, tty->name, "sx_write_room")) {
1778 func_exit();
1779 return 0;
1780 }
1781
1782 ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;
1783 if (ret < 0)
1784 ret = 0;
1785
1786 func_exit();
1787 return ret;
1788}
1789
1790
1791static int sx_chars_in_buffer(struct tty_struct *tty)
1792{
1793 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1794
1795 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08001796
Linus Torvalds1da177e2005-04-16 15:20:36 -07001797 if (sx_paranoia_check(port, tty->name, "sx_chars_in_buffer")) {
1798 func_exit();
1799 return 0;
1800 }
1801 func_exit();
1802 return port->xmit_cnt;
1803}
1804
1805
1806static void sx_flush_buffer(struct tty_struct *tty)
1807{
1808 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1809 unsigned long flags;
1810 struct specialix_board * bp;
1811
1812 func_enter();
1813
1814 if (sx_paranoia_check(port, tty->name, "sx_flush_buffer")) {
1815 func_exit();
1816 return;
1817 }
1818
1819 bp = port_Board(port);
1820 spin_lock_irqsave(&port->lock, flags);
1821 port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
1822 spin_unlock_irqrestore(&port->lock, flags);
1823 tty_wakeup(tty);
1824
1825 func_exit();
1826}
1827
1828
1829static int sx_tiocmget(struct tty_struct *tty, struct file *file)
1830{
1831 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1832 struct specialix_board * bp;
1833 unsigned char status;
1834 unsigned int result;
1835 unsigned long flags;
1836
1837 func_enter();
1838
1839 if (sx_paranoia_check(port, tty->name, __FUNCTION__)) {
1840 func_exit();
1841 return -ENODEV;
1842 }
1843
1844 bp = port_Board(port);
1845 spin_lock_irqsave (&bp->lock, flags);
1846 sx_out(bp, CD186x_CAR, port_No(port));
1847 status = sx_in(bp, CD186x_MSVR);
1848 spin_unlock_irqrestore(&bp->lock, flags);
1849 dprintk (SX_DEBUG_INIT, "Got msvr[%d] = %02x, car = %d.\n",
1850 port_No(port), status, sx_in (bp, CD186x_CAR));
1851 dprintk (SX_DEBUG_INIT, "sx_port = %p, port = %p\n", sx_port, port);
1852 if (SX_CRTSCTS(port->tty)) {
Jeff Garzikd61780c2005-10-30 15:01:51 -08001853 result = /* (status & MSVR_RTS) ? */ TIOCM_DTR /* : 0) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001854 | ((status & MSVR_DTR) ? TIOCM_RTS : 0)
1855 | ((status & MSVR_CD) ? TIOCM_CAR : 0)
1856 |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */
1857 | ((status & MSVR_CTS) ? TIOCM_CTS : 0);
1858 } else {
Jeff Garzikd61780c2005-10-30 15:01:51 -08001859 result = /* (status & MSVR_RTS) ? */ TIOCM_RTS /* : 0) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001860 | ((status & MSVR_DTR) ? TIOCM_DTR : 0)
1861 | ((status & MSVR_CD) ? TIOCM_CAR : 0)
1862 |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */
1863 | ((status & MSVR_CTS) ? TIOCM_CTS : 0);
1864 }
1865
1866 func_exit();
1867
1868 return result;
1869}
1870
1871
1872static int sx_tiocmset(struct tty_struct *tty, struct file *file,
1873 unsigned int set, unsigned int clear)
1874{
1875 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1876 unsigned long flags;
1877 struct specialix_board *bp;
1878
1879 func_enter();
1880
1881 if (sx_paranoia_check(port, tty->name, __FUNCTION__)) {
1882 func_exit();
1883 return -ENODEV;
1884 }
1885
1886 bp = port_Board(port);
1887
1888 spin_lock_irqsave(&port->lock, flags);
1889 /* if (set & TIOCM_RTS)
1890 port->MSVR |= MSVR_RTS; */
1891 /* if (set & TIOCM_DTR)
1892 port->MSVR |= MSVR_DTR; */
1893
1894 if (SX_CRTSCTS(port->tty)) {
1895 if (set & TIOCM_RTS)
1896 port->MSVR |= MSVR_DTR;
1897 } else {
1898 if (set & TIOCM_DTR)
1899 port->MSVR |= MSVR_DTR;
1900 }
1901
1902 /* if (clear & TIOCM_RTS)
1903 port->MSVR &= ~MSVR_RTS; */
1904 /* if (clear & TIOCM_DTR)
1905 port->MSVR &= ~MSVR_DTR; */
1906 if (SX_CRTSCTS(port->tty)) {
1907 if (clear & TIOCM_RTS)
1908 port->MSVR &= ~MSVR_DTR;
1909 } else {
1910 if (clear & TIOCM_DTR)
1911 port->MSVR &= ~MSVR_DTR;
1912 }
1913 spin_lock_irqsave(&bp->lock, flags);
1914 sx_out(bp, CD186x_CAR, port_No(port));
1915 sx_out(bp, CD186x_MSVR, port->MSVR);
1916 spin_unlock_irqrestore(&bp->lock, flags);
1917 spin_unlock_irqrestore(&port->lock, flags);
1918 func_exit();
1919 return 0;
1920}
1921
1922
1923static inline void sx_send_break(struct specialix_port * port, unsigned long length)
1924{
1925 struct specialix_board *bp = port_Board(port);
1926 unsigned long flags;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001927
Linus Torvalds1da177e2005-04-16 15:20:36 -07001928 func_enter();
1929
1930 spin_lock_irqsave (&port->lock, flags);
1931 port->break_length = SPECIALIX_TPS / HZ * length;
1932 port->COR2 |= COR2_ETC;
1933 port->IER |= IER_TXRDY;
1934 spin_lock_irqsave(&bp->lock, flags);
1935 sx_out(bp, CD186x_CAR, port_No(port));
1936 sx_out(bp, CD186x_COR2, port->COR2);
1937 sx_out(bp, CD186x_IER, port->IER);
1938 spin_unlock_irqrestore(&bp->lock, flags);
1939 spin_unlock_irqrestore (&port->lock, flags);
1940 sx_wait_CCR(bp);
1941 spin_lock_irqsave(&bp->lock, flags);
1942 sx_out(bp, CD186x_CCR, CCR_CORCHG2);
1943 spin_unlock_irqrestore(&bp->lock, flags);
1944 sx_wait_CCR(bp);
1945
1946 func_exit();
1947}
1948
1949
1950static inline int sx_set_serial_info(struct specialix_port * port,
1951 struct serial_struct __user * newinfo)
1952{
1953 struct serial_struct tmp;
1954 struct specialix_board *bp = port_Board(port);
1955 int change_speed;
1956
1957 func_enter();
1958 /*
Jesper Juhle49332b2005-05-01 08:59:08 -07001959 if (!access_ok(VERIFY_READ, (void *) newinfo, sizeof(tmp))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001960 func_exit();
Jesper Juhle49332b2005-05-01 08:59:08 -07001961 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001962 }
1963 */
1964 if (copy_from_user(&tmp, newinfo, sizeof(tmp))) {
1965 func_enter();
1966 return -EFAULT;
1967 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001968
1969#if 0
Linus Torvalds1da177e2005-04-16 15:20:36 -07001970 if ((tmp.irq != bp->irq) ||
1971 (tmp.port != bp->base) ||
1972 (tmp.type != PORT_CIRRUS) ||
1973 (tmp.baud_base != (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC) ||
1974 (tmp.custom_divisor != 0) ||
1975 (tmp.xmit_fifo_size != CD186x_NFIFO) ||
1976 (tmp.flags & ~SPECIALIX_LEGAL_FLAGS)) {
1977 func_exit();
1978 return -EINVAL;
1979 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001980#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001981
1982 change_speed = ((port->flags & ASYNC_SPD_MASK) !=
1983 (tmp.flags & ASYNC_SPD_MASK));
1984 change_speed |= (tmp.custom_divisor != port->custom_divisor);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001985
Linus Torvalds1da177e2005-04-16 15:20:36 -07001986 if (!capable(CAP_SYS_ADMIN)) {
1987 if ((tmp.close_delay != port->close_delay) ||
1988 (tmp.closing_wait != port->closing_wait) ||
1989 ((tmp.flags & ~ASYNC_USR_MASK) !=
1990 (port->flags & ~ASYNC_USR_MASK))) {
1991 func_exit();
1992 return -EPERM;
1993 }
1994 port->flags = ((port->flags & ~ASYNC_USR_MASK) |
1995 (tmp.flags & ASYNC_USR_MASK));
1996 port->custom_divisor = tmp.custom_divisor;
1997 } else {
1998 port->flags = ((port->flags & ~ASYNC_FLAGS) |
1999 (tmp.flags & ASYNC_FLAGS));
2000 port->close_delay = tmp.close_delay;
2001 port->closing_wait = tmp.closing_wait;
2002 port->custom_divisor = tmp.custom_divisor;
2003 }
2004 if (change_speed) {
2005 sx_change_speed(bp, port);
2006 }
2007 func_exit();
2008 return 0;
2009}
2010
2011
2012static inline int sx_get_serial_info(struct specialix_port * port,
2013 struct serial_struct __user *retinfo)
2014{
2015 struct serial_struct tmp;
2016 struct specialix_board *bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002017
Linus Torvalds1da177e2005-04-16 15:20:36 -07002018 func_enter();
2019
2020 /*
Jesper Juhle49332b2005-05-01 08:59:08 -07002021 if (!access_ok(VERIFY_WRITE, (void *) retinfo, sizeof(tmp)))
2022 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002023 */
2024
2025 memset(&tmp, 0, sizeof(tmp));
2026 tmp.type = PORT_CIRRUS;
2027 tmp.line = port - sx_port;
2028 tmp.port = bp->base;
2029 tmp.irq = bp->irq;
2030 tmp.flags = port->flags;
2031 tmp.baud_base = (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC;
2032 tmp.close_delay = port->close_delay * HZ/100;
2033 tmp.closing_wait = port->closing_wait * HZ/100;
2034 tmp.custom_divisor = port->custom_divisor;
2035 tmp.xmit_fifo_size = CD186x_NFIFO;
2036 if (copy_to_user(retinfo, &tmp, sizeof(tmp))) {
2037 func_exit();
2038 return -EFAULT;
2039 }
2040
2041 func_exit();
2042 return 0;
2043}
2044
2045
Jeff Garzikd61780c2005-10-30 15:01:51 -08002046static int sx_ioctl(struct tty_struct * tty, struct file * filp,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002047 unsigned int cmd, unsigned long arg)
2048{
2049 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2050 int retval;
2051 void __user *argp = (void __user *)arg;
2052
2053 func_enter();
2054
2055 if (sx_paranoia_check(port, tty->name, "sx_ioctl")) {
2056 func_exit();
2057 return -ENODEV;
2058 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002059
Linus Torvalds1da177e2005-04-16 15:20:36 -07002060 switch (cmd) {
2061 case TCSBRK: /* SVID version: non-zero arg --> no break */
2062 retval = tty_check_change(tty);
2063 if (retval) {
2064 func_exit();
2065 return retval;
2066 }
2067 tty_wait_until_sent(tty, 0);
2068 if (!arg)
2069 sx_send_break(port, HZ/4); /* 1/4 second */
2070 return 0;
2071 case TCSBRKP: /* support for POSIX tcsendbreak() */
2072 retval = tty_check_change(tty);
2073 if (retval) {
2074 func_exit();
2075 return retval;
2076 }
2077 tty_wait_until_sent(tty, 0);
2078 sx_send_break(port, arg ? arg*(HZ/10) : HZ/4);
2079 func_exit();
2080 return 0;
2081 case TIOCGSOFTCAR:
2082 if (put_user(C_CLOCAL(tty)?1:0, (unsigned long __user *)argp)) {
2083 func_exit();
2084 return -EFAULT;
2085 }
2086 func_exit();
2087 return 0;
2088 case TIOCSSOFTCAR:
2089 if (get_user(arg, (unsigned long __user *) argp)) {
2090 func_exit();
2091 return -EFAULT;
2092 }
2093 tty->termios->c_cflag =
2094 ((tty->termios->c_cflag & ~CLOCAL) |
2095 (arg ? CLOCAL : 0));
2096 func_exit();
2097 return 0;
2098 case TIOCGSERIAL:
2099 func_exit();
2100 return sx_get_serial_info(port, argp);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002101 case TIOCSSERIAL:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002102 func_exit();
2103 return sx_set_serial_info(port, argp);
2104 default:
2105 func_exit();
2106 return -ENOIOCTLCMD;
2107 }
2108 func_exit();
2109 return 0;
2110}
2111
2112
2113static void sx_throttle(struct tty_struct * tty)
2114{
2115 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2116 struct specialix_board *bp;
2117 unsigned long flags;
2118
2119 func_enter();
2120
2121 if (sx_paranoia_check(port, tty->name, "sx_throttle")) {
2122 func_exit();
2123 return;
2124 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002125
Linus Torvalds1da177e2005-04-16 15:20:36 -07002126 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002127
Linus Torvalds1da177e2005-04-16 15:20:36 -07002128 /* Use DTR instead of RTS ! */
Jeff Garzikd61780c2005-10-30 15:01:51 -08002129 if (SX_CRTSCTS (tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002130 port->MSVR &= ~MSVR_DTR;
2131 else {
2132 /* Auch!!! I think the system shouldn't call this then. */
2133 /* Or maybe we're supposed (allowed?) to do our side of hw
Jeff Garzikd61780c2005-10-30 15:01:51 -08002134 handshake anyway, even when hardware handshake is off.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002135 When you see this in your logs, please report.... */
2136 printk (KERN_ERR "sx%d: Need to throttle, but can't (hardware hs is off)\n",
2137 port_No (port));
2138 }
2139 spin_lock_irqsave(&bp->lock, flags);
2140 sx_out(bp, CD186x_CAR, port_No(port));
2141 spin_unlock_irqrestore(&bp->lock, flags);
2142 if (I_IXOFF(tty)) {
2143 spin_unlock_irqrestore(&bp->lock, flags);
2144 sx_wait_CCR(bp);
2145 spin_lock_irqsave(&bp->lock, flags);
2146 sx_out(bp, CD186x_CCR, CCR_SSCH2);
2147 spin_unlock_irqrestore(&bp->lock, flags);
2148 sx_wait_CCR(bp);
2149 }
2150 spin_lock_irqsave(&bp->lock, flags);
2151 sx_out(bp, CD186x_MSVR, port->MSVR);
2152 spin_unlock_irqrestore(&bp->lock, flags);
2153
2154 func_exit();
2155}
2156
2157
2158static void sx_unthrottle(struct tty_struct * tty)
2159{
2160 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2161 struct specialix_board *bp;
2162 unsigned long flags;
2163
2164 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002165
Linus Torvalds1da177e2005-04-16 15:20:36 -07002166 if (sx_paranoia_check(port, tty->name, "sx_unthrottle")) {
2167 func_exit();
2168 return;
2169 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002170
Linus Torvalds1da177e2005-04-16 15:20:36 -07002171 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002172
Linus Torvalds1da177e2005-04-16 15:20:36 -07002173 spin_lock_irqsave(&port->lock, flags);
2174 /* XXXX Use DTR INSTEAD???? */
2175 if (SX_CRTSCTS(tty)) {
2176 port->MSVR |= MSVR_DTR;
2177 } /* Else clause: see remark in "sx_throttle"... */
2178 spin_lock_irqsave(&bp->lock, flags);
2179 sx_out(bp, CD186x_CAR, port_No(port));
2180 spin_unlock_irqrestore(&bp->lock, flags);
2181 if (I_IXOFF(tty)) {
2182 spin_unlock_irqrestore(&port->lock, flags);
2183 sx_wait_CCR(bp);
2184 spin_lock_irqsave(&bp->lock, flags);
2185 sx_out(bp, CD186x_CCR, CCR_SSCH1);
2186 spin_unlock_irqrestore(&bp->lock, flags);
2187 sx_wait_CCR(bp);
2188 spin_lock_irqsave(&port->lock, flags);
2189 }
2190 spin_lock_irqsave(&bp->lock, flags);
2191 sx_out(bp, CD186x_MSVR, port->MSVR);
2192 spin_unlock_irqrestore(&bp->lock, flags);
2193 spin_unlock_irqrestore(&port->lock, flags);
2194
2195 func_exit();
2196}
2197
2198
2199static void sx_stop(struct tty_struct * tty)
2200{
2201 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2202 struct specialix_board *bp;
2203 unsigned long flags;
2204
2205 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002206
Linus Torvalds1da177e2005-04-16 15:20:36 -07002207 if (sx_paranoia_check(port, tty->name, "sx_stop")) {
2208 func_exit();
2209 return;
2210 }
2211
2212 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002213
Linus Torvalds1da177e2005-04-16 15:20:36 -07002214 spin_lock_irqsave(&port->lock, flags);
2215 port->IER &= ~IER_TXRDY;
2216 spin_lock_irqsave(&bp->lock, flags);
2217 sx_out(bp, CD186x_CAR, port_No(port));
2218 sx_out(bp, CD186x_IER, port->IER);
2219 spin_unlock_irqrestore(&bp->lock, flags);
2220 spin_unlock_irqrestore(&port->lock, flags);
2221
2222 func_exit();
2223}
2224
2225
2226static void sx_start(struct tty_struct * tty)
2227{
2228 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2229 struct specialix_board *bp;
2230 unsigned long flags;
2231
2232 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002233
Linus Torvalds1da177e2005-04-16 15:20:36 -07002234 if (sx_paranoia_check(port, tty->name, "sx_start")) {
2235 func_exit();
2236 return;
2237 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002238
Linus Torvalds1da177e2005-04-16 15:20:36 -07002239 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002240
Linus Torvalds1da177e2005-04-16 15:20:36 -07002241 spin_lock_irqsave(&port->lock, flags);
2242 if (port->xmit_cnt && port->xmit_buf && !(port->IER & IER_TXRDY)) {
2243 port->IER |= IER_TXRDY;
2244 spin_lock_irqsave(&bp->lock, flags);
2245 sx_out(bp, CD186x_CAR, port_No(port));
2246 sx_out(bp, CD186x_IER, port->IER);
2247 spin_unlock_irqrestore(&bp->lock, flags);
2248 }
2249 spin_unlock_irqrestore(&port->lock, flags);
2250
2251 func_exit();
2252}
2253
2254
2255/*
2256 * This routine is called from the work-queue when the interrupt
2257 * routine has signalled that a hangup has occurred. The path of
2258 * hangup processing is:
2259 *
2260 * serial interrupt routine -> (workqueue) ->
2261 * do_sx_hangup() -> tty->hangup() -> sx_hangup()
Jeff Garzikd61780c2005-10-30 15:01:51 -08002262 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07002263 */
David Howellsc4028952006-11-22 14:57:56 +00002264static void do_sx_hangup(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002265{
David Howellsc4028952006-11-22 14:57:56 +00002266 struct specialix_port *port =
2267 container_of(work, struct specialix_port, tqueue_hangup);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002268 struct tty_struct *tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002269
Linus Torvalds1da177e2005-04-16 15:20:36 -07002270 func_enter();
2271
2272 tty = port->tty;
2273 if (tty)
2274 tty_hangup(tty); /* FIXME: module removal race here */
2275
2276 func_exit();
2277}
2278
2279
2280static void sx_hangup(struct tty_struct * tty)
2281{
2282 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2283 struct specialix_board *bp;
2284 unsigned long flags;
2285
2286 func_enter();
2287
2288 if (sx_paranoia_check(port, tty->name, "sx_hangup")) {
2289 func_exit();
2290 return;
2291 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002292
Linus Torvalds1da177e2005-04-16 15:20:36 -07002293 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002294
Linus Torvalds1da177e2005-04-16 15:20:36 -07002295 sx_shutdown_port(bp, port);
2296 spin_lock_irqsave(&port->lock, flags);
2297 port->event = 0;
2298 bp->count -= port->count;
2299 if (bp->count < 0) {
2300 printk(KERN_ERR "sx%d: sx_hangup: bad board count: %d port: %d\n",
2301 board_No(bp), bp->count, tty->index);
2302 bp->count = 0;
2303 }
2304 port->count = 0;
2305 port->flags &= ~ASYNC_NORMAL_ACTIVE;
2306 port->tty = NULL;
2307 spin_unlock_irqrestore(&port->lock, flags);
2308 wake_up_interruptible(&port->open_wait);
2309
2310 func_exit();
2311}
2312
2313
Alan Cox606d0992006-12-08 02:38:45 -08002314static void sx_set_termios(struct tty_struct * tty, struct ktermios * old_termios)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002315{
2316 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2317 unsigned long flags;
2318 struct specialix_board * bp;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002319
Linus Torvalds1da177e2005-04-16 15:20:36 -07002320 if (sx_paranoia_check(port, tty->name, "sx_set_termios"))
2321 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002322
Linus Torvalds1da177e2005-04-16 15:20:36 -07002323 if (tty->termios->c_cflag == old_termios->c_cflag &&
2324 tty->termios->c_iflag == old_termios->c_iflag)
2325 return;
2326
2327 bp = port_Board(port);
2328 spin_lock_irqsave(&port->lock, flags);
2329 sx_change_speed(port_Board(port), port);
2330 spin_unlock_irqrestore(&port->lock, flags);
2331
2332 if ((old_termios->c_cflag & CRTSCTS) &&
2333 !(tty->termios->c_cflag & CRTSCTS)) {
2334 tty->hw_stopped = 0;
2335 sx_start(tty);
2336 }
2337}
2338
2339
David Howellsc4028952006-11-22 14:57:56 +00002340static void do_softint(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002341{
David Howellsc4028952006-11-22 14:57:56 +00002342 struct specialix_port *port =
2343 container_of(work, struct specialix_port, tqueue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002344 struct tty_struct *tty;
2345
2346 func_enter();
2347
2348 if(!(tty = port->tty)) {
2349 func_exit();
2350 return;
2351 }
2352
2353 if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) {
2354 tty_wakeup(tty);
2355 //wake_up_interruptible(&tty->write_wait);
2356 }
2357
2358 func_exit();
2359}
2360
Jeff Dikeb68e31d2006-10-02 02:17:18 -07002361static const struct tty_operations sx_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002362 .open = sx_open,
2363 .close = sx_close,
2364 .write = sx_write,
2365 .put_char = sx_put_char,
2366 .flush_chars = sx_flush_chars,
2367 .write_room = sx_write_room,
2368 .chars_in_buffer = sx_chars_in_buffer,
2369 .flush_buffer = sx_flush_buffer,
2370 .ioctl = sx_ioctl,
2371 .throttle = sx_throttle,
2372 .unthrottle = sx_unthrottle,
2373 .set_termios = sx_set_termios,
2374 .stop = sx_stop,
2375 .start = sx_start,
2376 .hangup = sx_hangup,
2377 .tiocmget = sx_tiocmget,
2378 .tiocmset = sx_tiocmset,
2379};
2380
2381static int sx_init_drivers(void)
2382{
2383 int error;
2384 int i;
2385
2386 func_enter();
2387
2388 specialix_driver = alloc_tty_driver(SX_NBOARD * SX_NPORT);
2389 if (!specialix_driver) {
2390 printk(KERN_ERR "sx: Couldn't allocate tty_driver.\n");
2391 func_exit();
2392 return 1;
2393 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002394
Linus Torvalds1da177e2005-04-16 15:20:36 -07002395 specialix_driver->owner = THIS_MODULE;
2396 specialix_driver->name = "ttyW";
2397 specialix_driver->major = SPECIALIX_NORMAL_MAJOR;
2398 specialix_driver->type = TTY_DRIVER_TYPE_SERIAL;
2399 specialix_driver->subtype = SERIAL_TYPE_NORMAL;
2400 specialix_driver->init_termios = tty_std_termios;
2401 specialix_driver->init_termios.c_cflag =
2402 B9600 | CS8 | CREAD | HUPCL | CLOCAL;
Alan Cox606d0992006-12-08 02:38:45 -08002403 specialix_driver->init_termios.c_ispeed = 9600;
2404 specialix_driver->init_termios.c_ospeed = 9600;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002405 specialix_driver->flags = TTY_DRIVER_REAL_RAW;
2406 tty_set_operations(specialix_driver, &sx_ops);
2407
2408 if ((error = tty_register_driver(specialix_driver))) {
2409 put_tty_driver(specialix_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002410 printk(KERN_ERR "sx: Couldn't register specialix IO8+ driver, error = %d\n",
2411 error);
2412 func_exit();
2413 return 1;
2414 }
2415 memset(sx_port, 0, sizeof(sx_port));
2416 for (i = 0; i < SX_NPORT * SX_NBOARD; i++) {
2417 sx_port[i].magic = SPECIALIX_MAGIC;
David Howellsc4028952006-11-22 14:57:56 +00002418 INIT_WORK(&sx_port[i].tqueue, do_softint);
2419 INIT_WORK(&sx_port[i].tqueue_hangup, do_sx_hangup);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002420 sx_port[i].close_delay = 50 * HZ/100;
2421 sx_port[i].closing_wait = 3000 * HZ/100;
2422 init_waitqueue_head(&sx_port[i].open_wait);
2423 init_waitqueue_head(&sx_port[i].close_wait);
2424 spin_lock_init(&sx_port[i].lock);
2425 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002426
Linus Torvalds1da177e2005-04-16 15:20:36 -07002427 func_exit();
2428 return 0;
2429}
2430
2431static void sx_release_drivers(void)
2432{
2433 func_enter();
2434
Linus Torvalds1da177e2005-04-16 15:20:36 -07002435 tty_unregister_driver(specialix_driver);
2436 put_tty_driver(specialix_driver);
2437 func_exit();
2438}
2439
Jeff Garzikd61780c2005-10-30 15:01:51 -08002440/*
2441 * This routine must be called by kernel at boot time
Linus Torvalds1da177e2005-04-16 15:20:36 -07002442 */
2443static int __init specialix_init(void)
2444{
2445 int i;
2446 int found = 0;
2447
2448 func_enter();
2449
2450 printk(KERN_INFO "sx: Specialix IO8+ driver v" VERSION ", (c) R.E.Wolff 1997/1998.\n");
2451 printk(KERN_INFO "sx: derived from work (c) D.Gorodchanin 1994-1996.\n");
2452#ifdef CONFIG_SPECIALIX_RTSCTS
2453 printk (KERN_INFO "sx: DTR/RTS pin is always RTS.\n");
2454#else
2455 printk (KERN_INFO "sx: DTR/RTS pin is RTS when CRTSCTS is on.\n");
2456#endif
Jeff Garzikd61780c2005-10-30 15:01:51 -08002457
Linus Torvalds1da177e2005-04-16 15:20:36 -07002458 for (i = 0; i < SX_NBOARD; i++)
Ingo Molnar34af9462006-06-27 02:53:55 -07002459 spin_lock_init(&sx_board[i].lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002460
2461 if (sx_init_drivers()) {
2462 func_exit();
2463 return -EIO;
2464 }
2465
Jeff Garzikd61780c2005-10-30 15:01:51 -08002466 for (i = 0; i < SX_NBOARD; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002467 if (sx_board[i].base && !sx_probe(&sx_board[i]))
2468 found++;
2469
2470#ifdef CONFIG_PCI
2471 {
2472 struct pci_dev *pdev = NULL;
2473
2474 i=0;
2475 while (i < SX_NBOARD) {
2476 if (sx_board[i].flags & SX_BOARD_PRESENT) {
2477 i++;
2478 continue;
2479 }
Alan Cox606d0992006-12-08 02:38:45 -08002480 pdev = pci_get_device (PCI_VENDOR_ID_SPECIALIX,
Jeff Garzikd61780c2005-10-30 15:01:51 -08002481 PCI_DEVICE_ID_SPECIALIX_IO8,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002482 pdev);
2483 if (!pdev) break;
2484
2485 if (pci_enable_device(pdev))
2486 continue;
2487
2488 sx_board[i].irq = pdev->irq;
2489
2490 sx_board[i].base = pci_resource_start (pdev, 2);
2491
2492 sx_board[i].flags |= SX_BOARD_IS_PCI;
2493 if (!sx_probe(&sx_board[i]))
2494 found ++;
2495 }
Alan Cox606d0992006-12-08 02:38:45 -08002496 /* May exit pci_get sequence early with lots of boards */
2497 if (pdev != NULL)
2498 pci_dev_put(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002499 }
2500#endif
2501
2502 if (!found) {
2503 sx_release_drivers();
2504 printk(KERN_INFO "sx: No specialix IO8+ boards detected.\n");
2505 func_exit();
2506 return -EIO;
2507 }
2508
2509 func_exit();
2510 return 0;
2511}
2512
2513static int iobase[SX_NBOARD] = {0,};
2514
2515static int irq [SX_NBOARD] = {0,};
2516
2517module_param_array(iobase, int, NULL, 0);
2518module_param_array(irq, int, NULL, 0);
2519module_param(sx_debug, int, 0);
2520module_param(sx_rxfifo, int, 0);
2521#ifdef SPECIALIX_TIMER
2522module_param(sx_poll, int, 0);
2523#endif
2524
2525/*
2526 * You can setup up to 4 boards.
2527 * by specifying "iobase=0xXXX,0xXXX ..." as insmod parameter.
Jeff Garzikd61780c2005-10-30 15:01:51 -08002528 * You should specify the IRQs too in that case "irq=....,...".
2529 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07002530 * More than 4 boards in one computer is not possible, as the card can
Jeff Garzikd61780c2005-10-30 15:01:51 -08002531 * only use 4 different interrupts.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002532 *
2533 */
2534static int __init specialix_init_module(void)
2535{
2536 int i;
2537
2538 func_enter();
2539
Linus Torvalds1da177e2005-04-16 15:20:36 -07002540 if (iobase[0] || iobase[1] || iobase[2] || iobase[3]) {
2541 for(i = 0; i < SX_NBOARD; i++) {
2542 sx_board[i].base = iobase[i];
2543 sx_board[i].irq = irq[i];
2544 sx_board[i].count= 0;
2545 }
2546 }
2547
2548 func_exit();
2549
2550 return specialix_init();
2551}
Jeff Garzikd61780c2005-10-30 15:01:51 -08002552
Linus Torvalds1da177e2005-04-16 15:20:36 -07002553static void __exit specialix_exit_module(void)
2554{
2555 int i;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002556
Linus Torvalds1da177e2005-04-16 15:20:36 -07002557 func_enter();
2558
2559 sx_release_drivers();
2560 for (i = 0; i < SX_NBOARD; i++)
Jeff Garzikd61780c2005-10-30 15:01:51 -08002561 if (sx_board[i].flags & SX_BOARD_PRESENT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002562 sx_release_io_range(&sx_board[i]);
2563#ifdef SPECIALIX_TIMER
2564 del_timer (&missed_irq_timer);
2565#endif
2566
2567 func_exit();
2568}
2569
Chuck Short7691030b2006-07-10 04:43:59 -07002570static struct pci_device_id specialx_pci_tbl[] __devinitdata = {
2571 { PCI_DEVICE(PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_SPECIALIX_IO8) },
2572 { }
2573};
2574MODULE_DEVICE_TABLE(pci, specialx_pci_tbl);
2575
Linus Torvalds1da177e2005-04-16 15:20:36 -07002576module_init(specialix_init_module);
2577module_exit(specialix_exit_module);
2578
2579MODULE_LICENSE("GPL");