blob: 4b5b5b78acb4835c3e58a212b92c87d035e512c5 [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
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181static struct tty_driver *specialix_driver;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183static struct specialix_board sx_board[SX_NBOARD] = {
184 { 0, SX_IOBASE1, 9, },
185 { 0, SX_IOBASE2, 11, },
186 { 0, SX_IOBASE3, 12, },
187 { 0, SX_IOBASE4, 15, },
188};
189
190static struct specialix_port sx_port[SX_NBOARD * SX_NPORT];
191
192
193#ifdef SPECIALIX_TIMER
194static struct timer_list missed_irq_timer;
David Howells7d12e782006-10-05 14:55:46 +0100195static irqreturn_t sx_interrupt(int irq, void * dev_id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196#endif
197
198
199
200static inline int sx_paranoia_check(struct specialix_port const * port,
201 char *name, const char *routine)
202{
203#ifdef SPECIALIX_PARANOIA_CHECK
204 static const char *badmagic =
205 KERN_ERR "sx: Warning: bad specialix port magic number for device %s in %s\n";
206 static const char *badinfo =
207 KERN_ERR "sx: Warning: null specialix port for device %s in %s\n";
Jeff Garzikd61780c2005-10-30 15:01:51 -0800208
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209 if (!port) {
210 printk(badinfo, name, routine);
211 return 1;
212 }
213 if (port->magic != SPECIALIX_MAGIC) {
214 printk(badmagic, name, routine);
215 return 1;
216 }
217#endif
218 return 0;
219}
220
221
222/*
Jeff Garzikd61780c2005-10-30 15:01:51 -0800223 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 * Service functions for specialix IO8+ driver.
Jeff Garzikd61780c2005-10-30 15:01:51 -0800225 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226 */
227
228/* Get board number from pointer */
229static inline int board_No (struct specialix_board * bp)
230{
231 return bp - sx_board;
232}
233
234
235/* Get port number from pointer */
236static inline int port_No (struct specialix_port const * port)
237{
Jeff Garzikd61780c2005-10-30 15:01:51 -0800238 return SX_PORT(port - sx_port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239}
240
241
242/* Get pointer to board from pointer to port */
243static inline struct specialix_board * port_Board(struct specialix_port const * port)
244{
245 return &sx_board[SX_BOARD(port - sx_port)];
246}
247
248
249/* Input Byte from CL CD186x register */
250static inline unsigned char sx_in(struct specialix_board * bp, unsigned short reg)
251{
252 bp->reg = reg | 0x80;
253 outb (reg | 0x80, bp->base + SX_ADDR_REG);
254 return inb (bp->base + SX_DATA_REG);
255}
256
257
258/* Output Byte to CL CD186x register */
259static inline void sx_out(struct specialix_board * bp, unsigned short reg,
260 unsigned char val)
261{
262 bp->reg = reg | 0x80;
263 outb (reg | 0x80, bp->base + SX_ADDR_REG);
264 outb (val, bp->base + SX_DATA_REG);
265}
266
267
268/* Input Byte from CL CD186x register */
269static inline unsigned char sx_in_off(struct specialix_board * bp, unsigned short reg)
270{
271 bp->reg = reg;
272 outb (reg, bp->base + SX_ADDR_REG);
273 return inb (bp->base + SX_DATA_REG);
274}
275
276
277/* Output Byte to CL CD186x register */
278static inline void sx_out_off(struct specialix_board * bp, unsigned short reg,
279 unsigned char val)
280{
281 bp->reg = reg;
282 outb (reg, bp->base + SX_ADDR_REG);
283 outb (val, bp->base + SX_DATA_REG);
284}
285
286
287/* Wait for Channel Command Register ready */
288static inline void sx_wait_CCR(struct specialix_board * bp)
289{
290 unsigned long delay, flags;
291 unsigned char ccr;
292
293 for (delay = SX_CCR_TIMEOUT; delay; delay--) {
294 spin_lock_irqsave(&bp->lock, flags);
295 ccr = sx_in(bp, CD186x_CCR);
296 spin_unlock_irqrestore(&bp->lock, flags);
297 if (!ccr)
298 return;
299 udelay (1);
300 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800301
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp));
303}
304
305
306/* Wait for Channel Command Register ready */
307static inline void sx_wait_CCR_off(struct specialix_board * bp)
308{
309 unsigned long delay;
310 unsigned char crr;
311 unsigned long flags;
312
313 for (delay = SX_CCR_TIMEOUT; delay; delay--) {
314 spin_lock_irqsave(&bp->lock, flags);
315 crr = sx_in_off(bp, CD186x_CCR);
316 spin_unlock_irqrestore(&bp->lock, flags);
317 if (!crr)
318 return;
319 udelay (1);
320 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800321
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp));
323}
324
325
326/*
327 * specialix IO8+ IO range functions.
328 */
329
Jeff Garzikd61780c2005-10-30 15:01:51 -0800330static inline int sx_request_io_range(struct specialix_board * bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331{
Jeff Garzikd61780c2005-10-30 15:01:51 -0800332 return request_region(bp->base,
333 bp->flags & SX_BOARD_IS_PCI ? SX_PCI_IO_SPACE : SX_IO_SPACE,
334 "specialix IO8+") == NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335}
336
337
338static inline void sx_release_io_range(struct specialix_board * bp)
339{
Jeff Garzikd61780c2005-10-30 15:01:51 -0800340 release_region(bp->base,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 bp->flags&SX_BOARD_IS_PCI?SX_PCI_IO_SPACE:SX_IO_SPACE);
342}
343
Jeff Garzikd61780c2005-10-30 15:01:51 -0800344
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345/* Set the IRQ using the RTS lines that run to the PAL on the board.... */
346static int sx_set_irq ( struct specialix_board *bp)
347{
348 int virq;
349 int i;
350 unsigned long flags;
351
Jeff Garzikd61780c2005-10-30 15:01:51 -0800352 if (bp->flags & SX_BOARD_IS_PCI)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353 return 1;
354 switch (bp->irq) {
355 /* In the same order as in the docs... */
356 case 15: virq = 0;break;
357 case 12: virq = 1;break;
358 case 11: virq = 2;break;
359 case 9: virq = 3;break;
360 default: printk (KERN_ERR "Speclialix: cannot set irq to %d.\n", bp->irq);
361 return 0;
362 }
363 spin_lock_irqsave(&bp->lock, flags);
364 for (i=0;i<2;i++) {
365 sx_out(bp, CD186x_CAR, i);
366 sx_out(bp, CD186x_MSVRTS, ((virq >> i) & 0x1)? MSVR_RTS:0);
367 }
368 spin_unlock_irqrestore(&bp->lock, flags);
369 return 1;
370}
371
372
373/* Reset and setup CD186x chip */
374static int sx_init_CD186x(struct specialix_board * bp)
375{
376 unsigned long flags;
377 int scaler;
378 int rv = 1;
379
380 func_enter();
381 sx_wait_CCR_off(bp); /* Wait for CCR ready */
382 spin_lock_irqsave(&bp->lock, flags);
383 sx_out_off(bp, CD186x_CCR, CCR_HARDRESET); /* Reset CD186x chip */
384 spin_unlock_irqrestore(&bp->lock, flags);
Jiri Slaby3e98cee2007-07-17 04:05:19 -0700385 msleep(50); /* Delay 0.05 sec */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386 spin_lock_irqsave(&bp->lock, flags);
387 sx_out_off(bp, CD186x_GIVR, SX_ID); /* Set ID for this chip */
388 sx_out_off(bp, CD186x_GICR, 0); /* Clear all bits */
389 sx_out_off(bp, CD186x_PILR1, SX_ACK_MINT); /* Prio for modem intr */
390 sx_out_off(bp, CD186x_PILR2, SX_ACK_TINT); /* Prio for transmitter intr */
391 sx_out_off(bp, CD186x_PILR3, SX_ACK_RINT); /* Prio for receiver intr */
392 /* Set RegAckEn */
393 sx_out_off(bp, CD186x_SRCR, sx_in (bp, CD186x_SRCR) | SRCR_REGACKEN);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800394
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395 /* Setting up prescaler. We need 4 ticks per 1 ms */
396 scaler = SX_OSCFREQ/SPECIALIX_TPS;
397
398 sx_out_off(bp, CD186x_PPRH, scaler >> 8);
399 sx_out_off(bp, CD186x_PPRL, scaler & 0xff);
400 spin_unlock_irqrestore(&bp->lock, flags);
401
402 if (!sx_set_irq (bp)) {
403 /* Figure out how to pass this along... */
404 printk (KERN_ERR "Cannot set irq to %d.\n", bp->irq);
405 rv = 0;
406 }
407
408 func_exit();
409 return rv;
410}
411
412
413static int read_cross_byte (struct specialix_board *bp, int reg, int bit)
414{
415 int i;
416 int t;
417 unsigned long flags;
418
419 spin_lock_irqsave(&bp->lock, flags);
420 for (i=0, t=0;i<8;i++) {
421 sx_out_off (bp, CD186x_CAR, i);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800422 if (sx_in_off (bp, reg) & bit)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423 t |= 1 << i;
424 }
425 spin_unlock_irqrestore(&bp->lock, flags);
426
427 return t;
428}
429
430
431#ifdef SPECIALIX_TIMER
432void missed_irq (unsigned long data)
433{
434 unsigned char irq;
435 unsigned long flags;
436 struct specialix_board *bp = (struct specialix_board *)data;
437
438 spin_lock_irqsave(&bp->lock, flags);
439 irq = sx_in ((struct specialix_board *)data, CD186x_SRSR) &
440 (SRSR_RREQint |
441 SRSR_TREQint |
442 SRSR_MREQint);
443 spin_unlock_irqrestore(&bp->lock, flags);
444 if (irq) {
445 printk (KERN_INFO "Missed interrupt... Calling int from timer. \n");
Jeff Garzika6f97b22007-10-31 05:20:49 -0400446 sx_interrupt (-1, bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447 }
Jiri Slaby40565f12007-02-12 00:52:31 -0800448 mod_timer(&missed_irq_timer, jiffies + sx_poll);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449}
450#endif
451
452
453
454/* Main probing routine, also sets irq. */
455static int sx_probe(struct specialix_board *bp)
456{
457 unsigned char val1, val2;
458#if 0
459 int irqs = 0;
460 int retries;
461#endif
462 int rev;
463 int chip;
464
465 func_enter();
466
Jeff Garzikd61780c2005-10-30 15:01:51 -0800467 if (sx_request_io_range(bp)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468 func_exit();
469 return 1;
470 }
471
472 /* Are the I/O ports here ? */
473 sx_out_off(bp, CD186x_PPRL, 0x5a);
474 short_pause ();
475 val1 = sx_in_off(bp, CD186x_PPRL);
476
477 sx_out_off(bp, CD186x_PPRL, 0xa5);
478 short_pause ();
479 val2 = sx_in_off(bp, CD186x_PPRL);
480
Jeff Garzikd61780c2005-10-30 15:01:51 -0800481
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 if ((val1 != 0x5a) || (val2 != 0xa5)) {
483 printk(KERN_INFO "sx%d: specialix IO8+ Board at 0x%03x not found.\n",
484 board_No(bp), bp->base);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800485 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486 func_exit();
487 return 1;
488 }
489
Jeff Garzikd61780c2005-10-30 15:01:51 -0800490 /* Check the DSR lines that Specialix uses as board
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491 identification */
492 val1 = read_cross_byte (bp, CD186x_MSVR, MSVR_DSR);
493 val2 = read_cross_byte (bp, CD186x_MSVR, MSVR_RTS);
494 dprintk (SX_DEBUG_INIT, "sx%d: DSR lines are: %02x, rts lines are: %02x\n",
495 board_No(bp), val1, val2);
496
497 /* They managed to switch the bit order between the docs and
498 the IO8+ card. The new PCI card now conforms to old docs.
499 They changed the PCI docs to reflect the situation on the
500 old card. */
501 val2 = (bp->flags & SX_BOARD_IS_PCI)?0x4d : 0xb2;
502 if (val1 != val2) {
503 printk(KERN_INFO "sx%d: specialix IO8+ ID %02x at 0x%03x not found (%02x).\n",
504 board_No(bp), val2, bp->base, val1);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800505 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506 func_exit();
507 return 1;
508 }
509
510
511#if 0
512 /* It's time to find IRQ for this board */
513 for (retries = 0; retries < 5 && irqs <= 0; retries++) {
514 irqs = probe_irq_on();
515 sx_init_CD186x(bp); /* Reset CD186x chip */
516 sx_out(bp, CD186x_CAR, 2); /* Select port 2 */
517 sx_wait_CCR(bp);
518 sx_out(bp, CD186x_CCR, CCR_TXEN); /* Enable transmitter */
519 sx_out(bp, CD186x_IER, IER_TXRDY); /* Enable tx empty intr */
Jiri Slaby3e98cee2007-07-17 04:05:19 -0700520 msleep(50);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 irqs = probe_irq_off(irqs);
522
523 dprintk (SX_DEBUG_INIT, "SRSR = %02x, ", sx_in(bp, CD186x_SRSR));
524 dprintk (SX_DEBUG_INIT, "TRAR = %02x, ", sx_in(bp, CD186x_TRAR));
525 dprintk (SX_DEBUG_INIT, "GIVR = %02x, ", sx_in(bp, CD186x_GIVR));
526 dprintk (SX_DEBUG_INIT, "GICR = %02x, ", sx_in(bp, CD186x_GICR));
527 dprintk (SX_DEBUG_INIT, "\n");
528
529 /* Reset CD186x again */
530 if (!sx_init_CD186x(bp)) {
531 /* Hmmm. This is dead code anyway. */
532 }
533
534 dprintk (SX_DEBUG_INIT "val1 = %02x, val2 = %02x, val3 = %02x.\n",
Jeff Garzikd61780c2005-10-30 15:01:51 -0800535 val1, val2, val3);
536
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800538
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539#if 0
540 if (irqs <= 0) {
541 printk(KERN_ERR "sx%d: Can't find IRQ for specialix IO8+ board at 0x%03x.\n",
542 board_No(bp), bp->base);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800543 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 func_exit();
545 return 1;
546 }
547#endif
548 printk (KERN_INFO "Started with irq=%d, but now have irq=%d.\n", bp->irq, irqs);
549 if (irqs > 0)
550 bp->irq = irqs;
551#endif
552 /* Reset CD186x again */
553 if (!sx_init_CD186x(bp)) {
Jeff Garzikd61780c2005-10-30 15:01:51 -0800554 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555 func_exit();
Jeff Garzikd61780c2005-10-30 15:01:51 -0800556 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557 }
558
559 sx_request_io_range(bp);
560 bp->flags |= SX_BOARD_PRESENT;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800561
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562 /* Chip revcode pkgtype
563 GFRCR SRCR bit 7
564 CD180 rev B 0x81 0
565 CD180 rev C 0x82 0
566 CD1864 rev A 0x82 1
Jeff Garzikd61780c2005-10-30 15:01:51 -0800567 CD1865 rev A 0x83 1 -- Do not use!!! Does not work.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568 CD1865 rev B 0x84 1
569 -- Thanks to Gwen Wang, Cirrus Logic.
570 */
571
572 switch (sx_in_off(bp, CD186x_GFRCR)) {
573 case 0x82:chip = 1864;rev='A';break;
574 case 0x83:chip = 1865;rev='A';break;
575 case 0x84:chip = 1865;rev='B';break;
576 case 0x85:chip = 1865;rev='C';break; /* Does not exist at this time */
577 default:chip=-1;rev='x';
578 }
579
580 dprintk (SX_DEBUG_INIT, " GFCR = 0x%02x\n", sx_in_off(bp, CD186x_GFRCR) );
581
582#ifdef SPECIALIX_TIMER
Jiri Slaby40565f12007-02-12 00:52:31 -0800583 setup_timer(&missed_irq_timer, missed_irq, (unsigned long)bp);
584 mod_timer(&missed_irq_timer, jiffies + sx_poll);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585#endif
586
587 printk(KERN_INFO"sx%d: specialix IO8+ board detected at 0x%03x, IRQ %d, CD%d Rev. %c.\n",
588 board_No(bp),
589 bp->base, bp->irq,
590 chip, rev);
591
592 func_exit();
593 return 0;
594}
595
Jeff Garzikd61780c2005-10-30 15:01:51 -0800596/*
597 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598 * Interrupt processing routines.
599 * */
600
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601static inline struct specialix_port * sx_get_port(struct specialix_board * bp,
602 unsigned char const * what)
603{
604 unsigned char channel;
605 struct specialix_port * port = NULL;
606
607 channel = sx_in(bp, CD186x_GICR) >> GICR_CHAN_OFF;
608 dprintk (SX_DEBUG_CHAN, "channel: %d\n", channel);
609 if (channel < CD186x_NCH) {
610 port = &sx_port[board_No(bp) * SX_NPORT + channel];
611 dprintk (SX_DEBUG_CHAN, "port: %d %p flags: 0x%x\n",board_No(bp) * SX_NPORT + channel, port, port->flags & ASYNC_INITIALIZED);
612
613 if (port->flags & ASYNC_INITIALIZED) {
614 dprintk (SX_DEBUG_CHAN, "port: %d %p\n", channel, port);
615 func_exit();
616 return port;
617 }
618 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800619 printk(KERN_INFO "sx%d: %s interrupt from invalid port %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 board_No(bp), what, channel);
621 return NULL;
622}
623
624
625static inline void sx_receive_exc(struct specialix_board * bp)
626{
627 struct specialix_port *port;
628 struct tty_struct *tty;
629 unsigned char status;
Alan Cox33f0f882006-01-09 20:54:13 -0800630 unsigned char ch, flag;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631
632 func_enter();
633
634 port = sx_get_port(bp, "Receive");
635 if (!port) {
636 dprintk (SX_DEBUG_RX, "Hmm, couldn't find port.\n");
637 func_exit();
638 return;
639 }
640 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800641
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 status = sx_in(bp, CD186x_RCSR);
643
644 dprintk (SX_DEBUG_RX, "status: 0x%x\n", status);
645 if (status & RCSR_OE) {
646 port->overrun++;
647 dprintk(SX_DEBUG_FIFO, "sx%d: port %d: Overrun. Total %ld overruns.\n",
648 board_No(bp), port_No(port), port->overrun);
649 }
650 status &= port->mark_mask;
651
652 /* This flip buffer check needs to be below the reading of the
653 status register to reset the chip's IRQ.... */
Alan Cox33f0f882006-01-09 20:54:13 -0800654 if (tty_buffer_request_room(tty, 1) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655 dprintk(SX_DEBUG_FIFO, "sx%d: port %d: Working around flip buffer overflow.\n",
656 board_No(bp), port_No(port));
657 func_exit();
658 return;
659 }
660
661 ch = sx_in(bp, CD186x_RDR);
662 if (!status) {
663 func_exit();
664 return;
665 }
666 if (status & RCSR_TOUT) {
Jeff Garzikd61780c2005-10-30 15:01:51 -0800667 printk(KERN_INFO "sx%d: port %d: Receiver timeout. Hardware problems ?\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668 board_No(bp), port_No(port));
669 func_exit();
670 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800671
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672 } else if (status & RCSR_BREAK) {
673 dprintk(SX_DEBUG_RX, "sx%d: port %d: Handling break...\n",
674 board_No(bp), port_No(port));
Alan Cox33f0f882006-01-09 20:54:13 -0800675 flag = TTY_BREAK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676 if (port->flags & ASYNC_SAK)
677 do_SAK(tty);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800678
679 } else if (status & RCSR_PE)
Alan Cox33f0f882006-01-09 20:54:13 -0800680 flag = TTY_PARITY;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800681
682 else if (status & RCSR_FE)
Alan Cox33f0f882006-01-09 20:54:13 -0800683 flag = TTY_FRAME;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800684
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685 else if (status & RCSR_OE)
Alan Cox33f0f882006-01-09 20:54:13 -0800686 flag = TTY_OVERRUN;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800687
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688 else
Alan Cox33f0f882006-01-09 20:54:13 -0800689 flag = TTY_NORMAL;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800690
Alan Cox33f0f882006-01-09 20:54:13 -0800691 if(tty_insert_flip_char(tty, ch, flag))
692 tty_flip_buffer_push(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693 func_exit();
694}
695
696
697static inline void sx_receive(struct specialix_board * bp)
698{
699 struct specialix_port *port;
700 struct tty_struct *tty;
701 unsigned char count;
702
703 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -0800704
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705 if (!(port = sx_get_port(bp, "Receive"))) {
706 dprintk (SX_DEBUG_RX, "Hmm, couldn't find port.\n");
707 func_exit();
708 return;
709 }
710 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800711
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712 count = sx_in(bp, CD186x_RDCR);
713 dprintk (SX_DEBUG_RX, "port: %p: count: %d\n", port, count);
714 port->hits[count > 8 ? 9 : count]++;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800715
Alan Cox33f0f882006-01-09 20:54:13 -0800716 tty_buffer_request_room(tty, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717
Alan Cox33f0f882006-01-09 20:54:13 -0800718 while (count--)
719 tty_insert_flip_char(tty, sx_in(bp, CD186x_RDR), TTY_NORMAL);
720 tty_flip_buffer_push(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721 func_exit();
722}
723
724
725static inline void sx_transmit(struct specialix_board * bp)
726{
727 struct specialix_port *port;
728 struct tty_struct *tty;
729 unsigned char count;
730
731 func_enter();
732 if (!(port = sx_get_port(bp, "Transmit"))) {
733 func_exit();
734 return;
735 }
736 dprintk (SX_DEBUG_TX, "port: %p\n", port);
737 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800738
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739 if (port->IER & IER_TXEMPTY) {
740 /* FIFO drained */
741 sx_out(bp, CD186x_CAR, port_No(port));
742 port->IER &= ~IER_TXEMPTY;
743 sx_out(bp, CD186x_IER, port->IER);
744 func_exit();
745 return;
746 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800747
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748 if ((port->xmit_cnt <= 0 && !port->break_length)
749 || tty->stopped || tty->hw_stopped) {
750 sx_out(bp, CD186x_CAR, port_No(port));
751 port->IER &= ~IER_TXRDY;
752 sx_out(bp, CD186x_IER, port->IER);
753 func_exit();
754 return;
755 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800756
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757 if (port->break_length) {
758 if (port->break_length > 0) {
759 if (port->COR2 & COR2_ETC) {
760 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
761 sx_out(bp, CD186x_TDR, CD186x_C_SBRK);
762 port->COR2 &= ~COR2_ETC;
763 }
764 count = min_t(int, port->break_length, 0xff);
765 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
766 sx_out(bp, CD186x_TDR, CD186x_C_DELAY);
767 sx_out(bp, CD186x_TDR, count);
768 if (!(port->break_length -= count))
769 port->break_length--;
770 } else {
771 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
772 sx_out(bp, CD186x_TDR, CD186x_C_EBRK);
773 sx_out(bp, CD186x_COR2, port->COR2);
774 sx_wait_CCR(bp);
775 sx_out(bp, CD186x_CCR, CCR_CORCHG2);
776 port->break_length = 0;
777 }
778
779 func_exit();
780 return;
781 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800782
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 count = CD186x_NFIFO;
784 do {
785 sx_out(bp, CD186x_TDR, port->xmit_buf[port->xmit_tail++]);
786 port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1);
787 if (--port->xmit_cnt <= 0)
788 break;
789 } while (--count > 0);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800790
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 if (port->xmit_cnt <= 0) {
792 sx_out(bp, CD186x_CAR, port_No(port));
793 port->IER &= ~IER_TXRDY;
794 sx_out(bp, CD186x_IER, port->IER);
795 }
796 if (port->xmit_cnt <= port->wakeup_chars)
Jiri Slabyd0d4e1c2008-02-07 00:16:39 -0800797 tty_wakeup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798
799 func_exit();
800}
801
802
803static inline void sx_check_modem(struct specialix_board * bp)
804{
805 struct specialix_port *port;
806 struct tty_struct *tty;
807 unsigned char mcr;
808 int msvr_cd;
809
810 dprintk (SX_DEBUG_SIGNALS, "Modem intr. ");
811 if (!(port = sx_get_port(bp, "Modem")))
812 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800813
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800815
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 mcr = sx_in(bp, CD186x_MCR);
817 printk ("mcr = %02x.\n", mcr);
818
819 if ((mcr & MCR_CDCHG)) {
820 dprintk (SX_DEBUG_SIGNALS, "CD just changed... ");
821 msvr_cd = sx_in(bp, CD186x_MSVR) & MSVR_CD;
822 if (msvr_cd) {
823 dprintk (SX_DEBUG_SIGNALS, "Waking up guys in open.\n");
824 wake_up_interruptible(&port->open_wait);
825 } else {
826 dprintk (SX_DEBUG_SIGNALS, "Sending HUP.\n");
Jiri Slabyd0d4e1c2008-02-07 00:16:39 -0800827 tty_hangup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 }
829 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800830
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
832 if (mcr & MCR_CTSCHG) {
833 if (sx_in(bp, CD186x_MSVR) & MSVR_CTS) {
834 tty->hw_stopped = 0;
835 port->IER |= IER_TXRDY;
836 if (port->xmit_cnt <= port->wakeup_chars)
Jiri Slabyd0d4e1c2008-02-07 00:16:39 -0800837 tty_wakeup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838 } else {
839 tty->hw_stopped = 1;
840 port->IER &= ~IER_TXRDY;
841 }
842 sx_out(bp, CD186x_IER, port->IER);
843 }
844 if (mcr & MCR_DSSXHG) {
845 if (sx_in(bp, CD186x_MSVR) & MSVR_DSR) {
846 tty->hw_stopped = 0;
847 port->IER |= IER_TXRDY;
848 if (port->xmit_cnt <= port->wakeup_chars)
Jiri Slabyd0d4e1c2008-02-07 00:16:39 -0800849 tty_wakeup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850 } else {
851 tty->hw_stopped = 1;
852 port->IER &= ~IER_TXRDY;
853 }
854 sx_out(bp, CD186x_IER, port->IER);
855 }
856#endif /* SPECIALIX_BRAIN_DAMAGED_CTS */
Jeff Garzikd61780c2005-10-30 15:01:51 -0800857
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858 /* Clear change bits */
859 sx_out(bp, CD186x_MCR, 0);
860}
861
862
863/* The main interrupt processing routine */
Jeff Garzika6f97b22007-10-31 05:20:49 -0400864static irqreturn_t sx_interrupt(int dummy, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865{
866 unsigned char status;
867 unsigned char ack;
Jeff Garzika6f97b22007-10-31 05:20:49 -0400868 struct specialix_board *bp = dev_id;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869 unsigned long loop = 0;
870 int saved_reg;
871 unsigned long flags;
872
873 func_enter();
874
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875 spin_lock_irqsave(&bp->lock, flags);
876
877 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 -0400878 if (!(bp->flags & SX_BOARD_ACTIVE)) {
Jeff Garzika6f97b22007-10-31 05:20:49 -0400879 dprintk (SX_DEBUG_IRQ, "sx: False interrupt. irq %d.\n", bp->irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880 spin_unlock_irqrestore(&bp->lock, flags);
881 func_exit();
882 return IRQ_NONE;
883 }
884
885 saved_reg = bp->reg;
886
887 while ((++loop < 16) && (status = (sx_in(bp, CD186x_SRSR) &
888 (SRSR_RREQint |
889 SRSR_TREQint |
Jeff Garzikd61780c2005-10-30 15:01:51 -0800890 SRSR_MREQint)))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891 if (status & SRSR_RREQint) {
892 ack = sx_in(bp, CD186x_RRAR);
893
894 if (ack == (SX_ID | GIVR_IT_RCV))
895 sx_receive(bp);
896 else if (ack == (SX_ID | GIVR_IT_REXC))
897 sx_receive_exc(bp);
898 else
899 printk(KERN_ERR "sx%d: status: 0x%x Bad receive ack 0x%02x.\n",
900 board_No(bp), status, ack);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800901
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902 } else if (status & SRSR_TREQint) {
903 ack = sx_in(bp, CD186x_TRAR);
904
905 if (ack == (SX_ID | GIVR_IT_TX))
906 sx_transmit(bp);
907 else
908 printk(KERN_ERR "sx%d: status: 0x%x Bad transmit ack 0x%02x. port: %d\n",
909 board_No(bp), status, ack, port_No (sx_get_port (bp, "Int")));
910 } else if (status & SRSR_MREQint) {
911 ack = sx_in(bp, CD186x_MRAR);
912
Jeff Garzikd61780c2005-10-30 15:01:51 -0800913 if (ack == (SX_ID | GIVR_IT_MODEM))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914 sx_check_modem(bp);
915 else
916 printk(KERN_ERR "sx%d: status: 0x%x Bad modem ack 0x%02x.\n",
917 board_No(bp), status, ack);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800918
919 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920
921 sx_out(bp, CD186x_EOIR, 0); /* Mark end of interrupt */
922 }
923 bp->reg = saved_reg;
924 outb (bp->reg, bp->base + SX_ADDR_REG);
925 spin_unlock_irqrestore(&bp->lock, flags);
926 func_exit();
927 return IRQ_HANDLED;
928}
929
930
931/*
932 * Routines for open & close processing.
933 */
934
935static void turn_ints_off (struct specialix_board *bp)
936{
937 unsigned long flags;
938
939 func_enter();
940 if (bp->flags & SX_BOARD_IS_PCI) {
941 /* This was intended for enabeling the interrupt on the
942 * PCI card. However it seems that it's already enabled
943 * and as PCI interrupts can be shared, there is no real
944 * reason to have to turn it off. */
945 }
946
947 spin_lock_irqsave(&bp->lock, flags);
948 (void) sx_in_off (bp, 0); /* Turn off interrupts. */
949 spin_unlock_irqrestore(&bp->lock, flags);
950
951 func_exit();
952}
953
954static void turn_ints_on (struct specialix_board *bp)
955{
956 unsigned long flags;
957
958 func_enter();
959
960 if (bp->flags & SX_BOARD_IS_PCI) {
961 /* play with the PCI chip. See comment above. */
962 }
963 spin_lock_irqsave(&bp->lock, flags);
964 (void) sx_in (bp, 0); /* Turn ON interrupts. */
965 spin_unlock_irqrestore(&bp->lock, flags);
966
967 func_exit();
968}
969
970
971/* Called with disabled interrupts */
972static inline int sx_setup_board(struct specialix_board * bp)
973{
974 int error;
975
Jeff Garzikd61780c2005-10-30 15:01:51 -0800976 if (bp->flags & SX_BOARD_ACTIVE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977 return 0;
978
979 if (bp->flags & SX_BOARD_IS_PCI)
Thomas Gleixner0f2ed4c2006-07-01 19:29:33 -0700980 error = request_irq(bp->irq, sx_interrupt, IRQF_DISABLED | IRQF_SHARED, "specialix IO8+", bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981 else
Thomas Gleixner0f2ed4c2006-07-01 19:29:33 -0700982 error = request_irq(bp->irq, sx_interrupt, IRQF_DISABLED, "specialix IO8+", bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983
Jeff Garzikd61780c2005-10-30 15:01:51 -0800984 if (error)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985 return error;
986
987 turn_ints_on (bp);
988 bp->flags |= SX_BOARD_ACTIVE;
989
990 return 0;
991}
992
993
994/* Called with disabled interrupts */
995static inline void sx_shutdown_board(struct specialix_board *bp)
996{
997 func_enter();
998
999 if (!(bp->flags & SX_BOARD_ACTIVE)) {
1000 func_exit();
1001 return;
1002 }
1003
1004 bp->flags &= ~SX_BOARD_ACTIVE;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001005
Linus Torvalds1da177e2005-04-16 15:20:36 -07001006 dprintk (SX_DEBUG_IRQ, "Freeing IRQ%d for board %d.\n",
1007 bp->irq, board_No (bp));
1008 free_irq(bp->irq, bp);
1009
1010 turn_ints_off (bp);
1011
1012
1013 func_exit();
1014}
1015
1016
1017/*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001018 * Setting up port characteristics.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019 * Must be called with disabled interrupts
1020 */
1021static void sx_change_speed(struct specialix_board *bp, struct specialix_port *port)
1022{
1023 struct tty_struct *tty;
1024 unsigned long baud;
1025 long tmp;
1026 unsigned char cor1 = 0, cor3 = 0;
1027 unsigned char mcor1 = 0, mcor2 = 0;
1028 static unsigned long again;
1029 unsigned long flags;
1030
1031 func_enter();
1032
1033 if (!(tty = port->tty) || !tty->termios) {
1034 func_exit();
1035 return;
1036 }
1037
1038 port->IER = 0;
1039 port->COR2 = 0;
1040 /* Select port on the board */
1041 spin_lock_irqsave(&bp->lock, flags);
1042 sx_out(bp, CD186x_CAR, port_No(port));
1043
1044 /* The Specialix board doens't implement the RTS lines.
1045 They are used to set the IRQ level. Don't touch them. */
1046 if (SX_CRTSCTS(tty))
1047 port->MSVR = MSVR_DTR | (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
1048 else
1049 port->MSVR = (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
1050 spin_unlock_irqrestore(&bp->lock, flags);
1051 dprintk (SX_DEBUG_TERMIOS, "sx: got MSVR=%02x.\n", port->MSVR);
Alan Cox67cc0162006-09-29 02:01:39 -07001052 baud = tty_get_baud_rate(tty);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001053
Alan Cox67cc0162006-09-29 02:01:39 -07001054 if (baud == 38400) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055 if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
Adrian Bunka4bb2cf2006-10-17 00:10:00 -07001056 baud = 57600;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057 if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
Adrian Bunka4bb2cf2006-10-17 00:10:00 -07001058 baud = 115200;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001060
Alan Cox67cc0162006-09-29 02:01:39 -07001061 if (!baud) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062 /* Drop DTR & exit */
1063 dprintk (SX_DEBUG_TERMIOS, "Dropping DTR... Hmm....\n");
1064 if (!SX_CRTSCTS (tty)) {
1065 port -> MSVR &= ~ MSVR_DTR;
1066 spin_lock_irqsave(&bp->lock, flags);
1067 sx_out(bp, CD186x_MSVR, port->MSVR );
1068 spin_unlock_irqrestore(&bp->lock, flags);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001069 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 else
1071 dprintk (SX_DEBUG_TERMIOS, "Can't drop DTR: no DTR.\n");
1072 return;
1073 } else {
1074 /* Set DTR on */
1075 if (!SX_CRTSCTS (tty)) {
1076 port ->MSVR |= MSVR_DTR;
1077 }
1078 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001079
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080 /*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001081 * Now we must calculate some speed depended things
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 */
1083
1084 /* Set baud rate for port */
1085 tmp = port->custom_divisor ;
1086 if ( tmp )
1087 printk (KERN_INFO "sx%d: Using custom baud rate divisor %ld. \n"
1088 "This is an untested option, please be carefull.\n",
1089 port_No (port), tmp);
1090 else
Alan Cox67cc0162006-09-29 02:01:39 -07001091 tmp = (((SX_OSCFREQ + baud/2) / baud +
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092 CD186x_TPC/2) / CD186x_TPC);
1093
Jeff Garzikd61780c2005-10-30 15:01:51 -08001094 if ((tmp < 0x10) && time_before(again, jiffies)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095 again = jiffies + HZ * 60;
1096 /* Page 48 of version 2.0 of the CL-CD1865 databook */
1097 if (tmp >= 12) {
1098 printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
1099 "Performance degradation is possible.\n"
1100 "Read specialix.txt for more info.\n",
1101 port_No (port), tmp);
1102 } else {
1103 printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
1104 "Warning: overstressing Cirrus chip. "
1105 "This might not work.\n"
Jeff Garzikd61780c2005-10-30 15:01:51 -08001106 "Read specialix.txt for more info.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001107 port_No (port), tmp);
1108 }
1109 }
1110 spin_lock_irqsave(&bp->lock, flags);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001111 sx_out(bp, CD186x_RBPRH, (tmp >> 8) & 0xff);
1112 sx_out(bp, CD186x_TBPRH, (tmp >> 8) & 0xff);
1113 sx_out(bp, CD186x_RBPRL, tmp & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114 sx_out(bp, CD186x_TBPRL, tmp & 0xff);
1115 spin_unlock_irqrestore(&bp->lock, flags);
Adrian Bunka4bb2cf2006-10-17 00:10:00 -07001116 if (port->custom_divisor)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117 baud = (SX_OSCFREQ + port->custom_divisor/2) / port->custom_divisor;
Adrian Bunka4bb2cf2006-10-17 00:10:00 -07001118 baud = (baud + 5) / 10; /* Estimated CPS */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001119
1120 /* Two timer ticks seems enough to wakeup something like SLIP driver */
Jeff Garzikd61780c2005-10-30 15:01:51 -08001121 tmp = ((baud + HZ/2) / HZ) * 2 - CD186x_NFIFO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001122 port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ?
1123 SERIAL_XMIT_SIZE - 1 : tmp);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001124
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125 /* Receiver timeout will be transmission time for 1.5 chars */
1126 tmp = (SPECIALIX_TPS + SPECIALIX_TPS/2 + baud/2) / baud;
1127 tmp = (tmp > 0xff) ? 0xff : tmp;
1128 spin_lock_irqsave(&bp->lock, flags);
1129 sx_out(bp, CD186x_RTPR, tmp);
1130 spin_unlock_irqrestore(&bp->lock, flags);
1131 switch (C_CSIZE(tty)) {
1132 case CS5:
1133 cor1 |= COR1_5BITS;
1134 break;
1135 case CS6:
1136 cor1 |= COR1_6BITS;
1137 break;
1138 case CS7:
1139 cor1 |= COR1_7BITS;
1140 break;
1141 case CS8:
1142 cor1 |= COR1_8BITS;
1143 break;
1144 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001145
1146 if (C_CSTOPB(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147 cor1 |= COR1_2SB;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001148
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 cor1 |= COR1_IGNORE;
1150 if (C_PARENB(tty)) {
1151 cor1 |= COR1_NORMPAR;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001152 if (C_PARODD(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153 cor1 |= COR1_ODDP;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001154 if (I_INPCK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155 cor1 &= ~COR1_IGNORE;
1156 }
1157 /* Set marking of some errors */
1158 port->mark_mask = RCSR_OE | RCSR_TOUT;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001159 if (I_INPCK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 port->mark_mask |= RCSR_FE | RCSR_PE;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001161 if (I_BRKINT(tty) || I_PARMRK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 port->mark_mask |= RCSR_BREAK;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001163 if (I_IGNPAR(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 port->mark_mask &= ~(RCSR_FE | RCSR_PE);
1165 if (I_IGNBRK(tty)) {
1166 port->mark_mask &= ~RCSR_BREAK;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001167 if (I_IGNPAR(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168 /* Real raw mode. Ignore all */
1169 port->mark_mask &= ~RCSR_OE;
1170 }
1171 /* Enable Hardware Flow Control */
1172 if (C_CRTSCTS(tty)) {
1173#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
1174 port->IER |= IER_DSR | IER_CTS;
1175 mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD;
1176 mcor2 |= MCOR2_DSROD | MCOR2_CTSOD;
1177 spin_lock_irqsave(&bp->lock, flags);
1178 tty->hw_stopped = !(sx_in(bp, CD186x_MSVR) & (MSVR_CTS|MSVR_DSR));
1179 spin_unlock_irqrestore(&bp->lock, flags);
1180#else
Jeff Garzikd61780c2005-10-30 15:01:51 -08001181 port->COR2 |= COR2_CTSAE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182#endif
1183 }
1184 /* Enable Software Flow Control. FIXME: I'm not sure about this */
1185 /* Some people reported that it works, but I still doubt it */
1186 if (I_IXON(tty)) {
1187 port->COR2 |= COR2_TXIBE;
1188 cor3 |= (COR3_FCT | COR3_SCDE);
1189 if (I_IXANY(tty))
1190 port->COR2 |= COR2_IXM;
1191 spin_lock_irqsave(&bp->lock, flags);
1192 sx_out(bp, CD186x_SCHR1, START_CHAR(tty));
1193 sx_out(bp, CD186x_SCHR2, STOP_CHAR(tty));
1194 sx_out(bp, CD186x_SCHR3, START_CHAR(tty));
1195 sx_out(bp, CD186x_SCHR4, STOP_CHAR(tty));
1196 spin_unlock_irqrestore(&bp->lock, flags);
1197 }
1198 if (!C_CLOCAL(tty)) {
1199 /* Enable CD check */
1200 port->IER |= IER_CD;
1201 mcor1 |= MCOR1_CDZD;
1202 mcor2 |= MCOR2_CDOD;
1203 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001204
1205 if (C_CREAD(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206 /* Enable receiver */
1207 port->IER |= IER_RXD;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001208
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209 /* Set input FIFO size (1-8 bytes) */
1210 cor3 |= sx_rxfifo;
1211 /* Setting up CD186x channel registers */
1212 spin_lock_irqsave(&bp->lock, flags);
1213 sx_out(bp, CD186x_COR1, cor1);
1214 sx_out(bp, CD186x_COR2, port->COR2);
1215 sx_out(bp, CD186x_COR3, cor3);
1216 spin_unlock_irqrestore(&bp->lock, flags);
1217 /* Make CD186x know about registers change */
1218 sx_wait_CCR(bp);
1219 spin_lock_irqsave(&bp->lock, flags);
1220 sx_out(bp, CD186x_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3);
1221 /* Setting up modem option registers */
1222 dprintk (SX_DEBUG_TERMIOS, "Mcor1 = %02x, mcor2 = %02x.\n", mcor1, mcor2);
1223 sx_out(bp, CD186x_MCOR1, mcor1);
1224 sx_out(bp, CD186x_MCOR2, mcor2);
1225 spin_unlock_irqrestore(&bp->lock, flags);
1226 /* Enable CD186x transmitter & receiver */
1227 sx_wait_CCR(bp);
1228 spin_lock_irqsave(&bp->lock, flags);
1229 sx_out(bp, CD186x_CCR, CCR_TXEN | CCR_RXEN);
1230 /* Enable interrupts */
1231 sx_out(bp, CD186x_IER, port->IER);
1232 /* And finally set the modem lines... */
1233 sx_out(bp, CD186x_MSVR, port->MSVR);
1234 spin_unlock_irqrestore(&bp->lock, flags);
1235
1236 func_exit();
1237}
1238
1239
1240/* Must be called with interrupts enabled */
1241static int sx_setup_port(struct specialix_board *bp, struct specialix_port *port)
1242{
1243 unsigned long flags;
1244
1245 func_enter();
1246
1247 if (port->flags & ASYNC_INITIALIZED) {
1248 func_exit();
1249 return 0;
1250 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001251
Linus Torvalds1da177e2005-04-16 15:20:36 -07001252 if (!port->xmit_buf) {
1253 /* We may sleep in get_zeroed_page() */
1254 unsigned long tmp;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001255
Linus Torvalds1da177e2005-04-16 15:20:36 -07001256 if (!(tmp = get_zeroed_page(GFP_KERNEL))) {
1257 func_exit();
1258 return -ENOMEM;
1259 }
1260
1261 if (port->xmit_buf) {
1262 free_page(tmp);
1263 func_exit();
1264 return -ERESTARTSYS;
1265 }
1266 port->xmit_buf = (unsigned char *) tmp;
1267 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001268
Linus Torvalds1da177e2005-04-16 15:20:36 -07001269 spin_lock_irqsave(&port->lock, flags);
1270
Jeff Garzikd61780c2005-10-30 15:01:51 -08001271 if (port->tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001272 clear_bit(TTY_IO_ERROR, &port->tty->flags);
1273
1274 port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
1275 sx_change_speed(bp, port);
1276 port->flags |= ASYNC_INITIALIZED;
1277
1278 spin_unlock_irqrestore(&port->lock, flags);
1279
Jeff Garzikd61780c2005-10-30 15:01:51 -08001280
Linus Torvalds1da177e2005-04-16 15:20:36 -07001281 func_exit();
1282 return 0;
1283}
1284
1285
1286/* Must be called with interrupts disabled */
1287static void sx_shutdown_port(struct specialix_board *bp, struct specialix_port *port)
1288{
1289 struct tty_struct *tty;
1290 int i;
1291 unsigned long flags;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001292
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293 func_enter();
1294
1295 if (!(port->flags & ASYNC_INITIALIZED)) {
1296 func_exit();
1297 return;
1298 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001299
Linus Torvalds1da177e2005-04-16 15:20:36 -07001300 if (sx_debug & SX_DEBUG_FIFO) {
1301 dprintk(SX_DEBUG_FIFO, "sx%d: port %d: %ld overruns, FIFO hits [ ",
1302 board_No(bp), port_No(port), port->overrun);
1303 for (i = 0; i < 10; i++) {
1304 dprintk(SX_DEBUG_FIFO, "%ld ", port->hits[i]);
1305 }
1306 dprintk(SX_DEBUG_FIFO, "].\n");
1307 }
1308
1309 if (port->xmit_buf) {
1310 free_page((unsigned long) port->xmit_buf);
1311 port->xmit_buf = NULL;
1312 }
1313
1314 /* Select port */
1315 spin_lock_irqsave(&bp->lock, flags);
1316 sx_out(bp, CD186x_CAR, port_No(port));
1317
1318 if (!(tty = port->tty) || C_HUPCL(tty)) {
1319 /* Drop DTR */
1320 sx_out(bp, CD186x_MSVDTR, 0);
1321 }
1322 spin_unlock_irqrestore(&bp->lock, flags);
1323 /* Reset port */
1324 sx_wait_CCR(bp);
1325 spin_lock_irqsave(&bp->lock, flags);
1326 sx_out(bp, CD186x_CCR, CCR_SOFTRESET);
1327 /* Disable all interrupts from this port */
1328 port->IER = 0;
1329 sx_out(bp, CD186x_IER, port->IER);
1330 spin_unlock_irqrestore(&bp->lock, flags);
1331 if (tty)
1332 set_bit(TTY_IO_ERROR, &tty->flags);
1333 port->flags &= ~ASYNC_INITIALIZED;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001334
1335 if (!bp->count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001336 sx_shutdown_board(bp);
1337 func_exit();
1338}
1339
Jeff Garzikd61780c2005-10-30 15:01:51 -08001340
Linus Torvalds1da177e2005-04-16 15:20:36 -07001341static int block_til_ready(struct tty_struct *tty, struct file * filp,
1342 struct specialix_port *port)
1343{
1344 DECLARE_WAITQUEUE(wait, current);
1345 struct specialix_board *bp = port_Board(port);
1346 int retval;
1347 int do_clocal = 0;
1348 int CD;
1349 unsigned long flags;
1350
1351 func_enter();
1352
1353 /*
1354 * If the device is in the middle of being closed, then block
1355 * until it's done, and then try again.
1356 */
1357 if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {
1358 interruptible_sleep_on(&port->close_wait);
1359 if (port->flags & ASYNC_HUP_NOTIFY) {
1360 func_exit();
1361 return -EAGAIN;
1362 } else {
1363 func_exit();
1364 return -ERESTARTSYS;
1365 }
1366 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001367
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368 /*
1369 * If non-blocking mode is set, or the port is not enabled,
1370 * then make the check up front and then exit.
1371 */
1372 if ((filp->f_flags & O_NONBLOCK) ||
1373 (tty->flags & (1 << TTY_IO_ERROR))) {
1374 port->flags |= ASYNC_NORMAL_ACTIVE;
1375 func_exit();
1376 return 0;
1377 }
1378
1379 if (C_CLOCAL(tty))
1380 do_clocal = 1;
1381
1382 /*
1383 * Block waiting for the carrier detect and the line to become
1384 * free (i.e., not in use by the callout). While we are in
1385 * this loop, info->count is dropped by one, so that
1386 * rs_close() knows when to free things. We restore it upon
1387 * exit, either normal or abnormal.
1388 */
1389 retval = 0;
1390 add_wait_queue(&port->open_wait, &wait);
1391 spin_lock_irqsave(&port->lock, flags);
1392 if (!tty_hung_up_p(filp)) {
1393 port->count--;
1394 }
1395 spin_unlock_irqrestore(&port->lock, flags);
1396 port->blocked_open++;
1397 while (1) {
1398 spin_lock_irqsave(&bp->lock, flags);
1399 sx_out(bp, CD186x_CAR, port_No(port));
1400 CD = sx_in(bp, CD186x_MSVR) & MSVR_CD;
1401 if (SX_CRTSCTS (tty)) {
1402 /* Activate RTS */
1403 port->MSVR |= MSVR_DTR; /* WTF? */
1404 sx_out (bp, CD186x_MSVR, port->MSVR);
1405 } else {
1406 /* Activate DTR */
1407 port->MSVR |= MSVR_DTR;
1408 sx_out (bp, CD186x_MSVR, port->MSVR);
1409 }
1410 spin_unlock_irqrestore(&bp->lock, flags);
1411 set_current_state(TASK_INTERRUPTIBLE);
1412 if (tty_hung_up_p(filp) ||
1413 !(port->flags & ASYNC_INITIALIZED)) {
1414 if (port->flags & ASYNC_HUP_NOTIFY)
1415 retval = -EAGAIN;
1416 else
Jeff Garzikd61780c2005-10-30 15:01:51 -08001417 retval = -ERESTARTSYS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001418 break;
1419 }
1420 if (!(port->flags & ASYNC_CLOSING) &&
1421 (do_clocal || CD))
1422 break;
1423 if (signal_pending(current)) {
1424 retval = -ERESTARTSYS;
1425 break;
1426 }
1427 schedule();
1428 }
1429
1430 set_current_state(TASK_RUNNING);
1431 remove_wait_queue(&port->open_wait, &wait);
1432 spin_lock_irqsave(&port->lock, flags);
1433 if (!tty_hung_up_p(filp)) {
1434 port->count++;
1435 }
1436 port->blocked_open--;
1437 spin_unlock_irqrestore(&port->lock, flags);
1438 if (retval) {
1439 func_exit();
1440 return retval;
1441 }
1442
1443 port->flags |= ASYNC_NORMAL_ACTIVE;
1444 func_exit();
1445 return 0;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001446}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447
1448
1449static int sx_open(struct tty_struct * tty, struct file * filp)
1450{
1451 int board;
1452 int error;
1453 struct specialix_port * port;
1454 struct specialix_board * bp;
1455 int i;
1456 unsigned long flags;
1457
1458 func_enter();
1459
1460 board = SX_BOARD(tty->index);
1461
1462 if (board >= SX_NBOARD || !(sx_board[board].flags & SX_BOARD_PRESENT)) {
1463 func_exit();
1464 return -ENODEV;
1465 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001466
Linus Torvalds1da177e2005-04-16 15:20:36 -07001467 bp = &sx_board[board];
1468 port = sx_port + board * SX_NPORT + SX_PORT(tty->index);
1469 port->overrun = 0;
1470 for (i = 0; i < 10; i++)
1471 port->hits[i]=0;
1472
1473 dprintk (SX_DEBUG_OPEN, "Board = %d, bp = %p, port = %p, portno = %d.\n",
1474 board, bp, port, SX_PORT(tty->index));
1475
1476 if (sx_paranoia_check(port, tty->name, "sx_open")) {
1477 func_enter();
1478 return -ENODEV;
1479 }
1480
1481 if ((error = sx_setup_board(bp))) {
1482 func_exit();
1483 return error;
1484 }
1485
1486 spin_lock_irqsave(&bp->lock, flags);
1487 port->count++;
1488 bp->count++;
1489 tty->driver_data = port;
1490 port->tty = tty;
1491 spin_unlock_irqrestore(&bp->lock, flags);
1492
1493 if ((error = sx_setup_port(bp, port))) {
1494 func_enter();
1495 return error;
1496 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001497
Linus Torvalds1da177e2005-04-16 15:20:36 -07001498 if ((error = block_til_ready(tty, filp, port))) {
1499 func_enter();
1500 return error;
1501 }
1502
1503 func_exit();
1504 return 0;
1505}
1506
1507
1508static void sx_close(struct tty_struct * tty, struct file * filp)
1509{
1510 struct specialix_port *port = (struct specialix_port *) tty->driver_data;
1511 struct specialix_board *bp;
1512 unsigned long flags;
1513 unsigned long timeout;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001514
Linus Torvalds1da177e2005-04-16 15:20:36 -07001515 func_enter();
1516 if (!port || sx_paranoia_check(port, tty->name, "close")) {
1517 func_exit();
1518 return;
1519 }
1520 spin_lock_irqsave(&port->lock, flags);
1521
1522 if (tty_hung_up_p(filp)) {
1523 spin_unlock_irqrestore(&port->lock, flags);
1524 func_exit();
1525 return;
1526 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001527
Linus Torvalds1da177e2005-04-16 15:20:36 -07001528 bp = port_Board(port);
1529 if ((tty->count == 1) && (port->count != 1)) {
1530 printk(KERN_ERR "sx%d: sx_close: bad port count;"
1531 " tty->count is 1, port count is %d\n",
1532 board_No(bp), port->count);
1533 port->count = 1;
1534 }
1535
1536 if (port->count > 1) {
1537 port->count--;
1538 bp->count--;
1539
1540 spin_unlock_irqrestore(&port->lock, flags);
1541
1542 func_exit();
1543 return;
1544 }
1545 port->flags |= ASYNC_CLOSING;
1546 /*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001547 * Now we wait for the transmit buffer to clear; and we notify
Linus Torvalds1da177e2005-04-16 15:20:36 -07001548 * the line discipline to only process XON/XOFF characters.
1549 */
1550 tty->closing = 1;
1551 spin_unlock_irqrestore(&port->lock, flags);
1552 dprintk (SX_DEBUG_OPEN, "Closing\n");
1553 if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) {
1554 tty_wait_until_sent(tty, port->closing_wait);
1555 }
1556 /*
1557 * At this point we stop accepting input. To do this, we
1558 * disable the receive line status interrupts, and tell the
1559 * interrupt driver to stop checking the data ready bit in the
1560 * line status register.
1561 */
1562 dprintk (SX_DEBUG_OPEN, "Closed\n");
1563 port->IER &= ~IER_RXD;
1564 if (port->flags & ASYNC_INITIALIZED) {
1565 port->IER &= ~IER_TXRDY;
1566 port->IER |= IER_TXEMPTY;
1567 spin_lock_irqsave(&bp->lock, flags);
1568 sx_out(bp, CD186x_CAR, port_No(port));
1569 sx_out(bp, CD186x_IER, port->IER);
1570 spin_unlock_irqrestore(&bp->lock, flags);
1571 /*
1572 * Before we drop DTR, make sure the UART transmitter
1573 * has completely drained; this is especially
1574 * important if there is a transmit FIFO!
1575 */
1576 timeout = jiffies+HZ;
1577 while(port->IER & IER_TXEMPTY) {
1578 set_current_state (TASK_INTERRUPTIBLE);
1579 msleep_interruptible(jiffies_to_msecs(port->timeout));
1580 if (time_after(jiffies, timeout)) {
1581 printk (KERN_INFO "Timeout waiting for close\n");
1582 break;
1583 }
1584 }
1585
1586 }
1587
1588 if (--bp->count < 0) {
1589 printk(KERN_ERR "sx%d: sx_shutdown_port: bad board count: %d port: %d\n",
1590 board_No(bp), bp->count, tty->index);
1591 bp->count = 0;
1592 }
1593 if (--port->count < 0) {
1594 printk(KERN_ERR "sx%d: sx_close: bad port count for tty%d: %d\n",
1595 board_No(bp), port_No(port), port->count);
1596 port->count = 0;
1597 }
1598
1599 sx_shutdown_port(bp, port);
1600 if (tty->driver->flush_buffer)
1601 tty->driver->flush_buffer(tty);
1602 tty_ldisc_flush(tty);
1603 spin_lock_irqsave(&port->lock, flags);
1604 tty->closing = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001605 port->tty = NULL;
1606 spin_unlock_irqrestore(&port->lock, flags);
1607 if (port->blocked_open) {
1608 if (port->close_delay) {
1609 msleep_interruptible(jiffies_to_msecs(port->close_delay));
1610 }
1611 wake_up_interruptible(&port->open_wait);
1612 }
1613 port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
1614 wake_up_interruptible(&port->close_wait);
1615
1616 func_exit();
1617}
1618
1619
Jeff Garzikd61780c2005-10-30 15:01:51 -08001620static int sx_write(struct tty_struct * tty,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001621 const unsigned char *buf, int count)
1622{
1623 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1624 struct specialix_board *bp;
1625 int c, total = 0;
1626 unsigned long flags;
1627
1628 func_enter();
1629 if (sx_paranoia_check(port, tty->name, "sx_write")) {
1630 func_exit();
1631 return 0;
1632 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001633
Linus Torvalds1da177e2005-04-16 15:20:36 -07001634 bp = port_Board(port);
1635
Jiri Slaby365e0222006-09-30 23:28:11 -07001636 if (!port->xmit_buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001637 func_exit();
1638 return 0;
1639 }
1640
1641 while (1) {
1642 spin_lock_irqsave(&port->lock, flags);
1643 c = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
1644 SERIAL_XMIT_SIZE - port->xmit_head));
1645 if (c <= 0) {
1646 spin_unlock_irqrestore(&port->lock, flags);
1647 break;
1648 }
1649 memcpy(port->xmit_buf + port->xmit_head, buf, c);
1650 port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
1651 port->xmit_cnt += c;
1652 spin_unlock_irqrestore(&port->lock, flags);
1653
1654 buf += c;
1655 count -= c;
1656 total += c;
1657 }
1658
1659 spin_lock_irqsave(&bp->lock, flags);
1660 if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
1661 !(port->IER & IER_TXRDY)) {
1662 port->IER |= IER_TXRDY;
1663 sx_out(bp, CD186x_CAR, port_No(port));
1664 sx_out(bp, CD186x_IER, port->IER);
1665 }
1666 spin_unlock_irqrestore(&bp->lock, flags);
1667 func_exit();
1668
1669 return total;
1670}
1671
1672
1673static void sx_put_char(struct tty_struct * tty, unsigned char ch)
1674{
1675 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1676 unsigned long flags;
1677 struct specialix_board * bp;
1678
1679 func_enter();
1680
1681 if (sx_paranoia_check(port, tty->name, "sx_put_char")) {
1682 func_exit();
1683 return;
1684 }
1685 dprintk (SX_DEBUG_TX, "check tty: %p %p\n", tty, port->xmit_buf);
Eric Sesterhenn326f28e92006-06-25 05:48:48 -07001686 if (!port->xmit_buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001687 func_exit();
1688 return;
1689 }
1690 bp = port_Board(port);
1691 spin_lock_irqsave(&port->lock, flags);
1692
1693 dprintk (SX_DEBUG_TX, "xmit_cnt: %d xmit_buf: %p\n", port->xmit_cnt, port->xmit_buf);
1694 if ((port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) || (!port->xmit_buf)) {
1695 spin_unlock_irqrestore(&port->lock, flags);
1696 dprintk (SX_DEBUG_TX, "Exit size\n");
1697 func_exit();
1698 return;
1699 }
1700 dprintk (SX_DEBUG_TX, "Handle xmit: %p %p\n", port, port->xmit_buf);
1701 port->xmit_buf[port->xmit_head++] = ch;
1702 port->xmit_head &= SERIAL_XMIT_SIZE - 1;
1703 port->xmit_cnt++;
1704 spin_unlock_irqrestore(&port->lock, flags);
1705
1706 func_exit();
1707}
1708
1709
1710static void sx_flush_chars(struct tty_struct * tty)
1711{
1712 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1713 unsigned long flags;
1714 struct specialix_board * bp = port_Board(port);
1715
1716 func_enter();
1717
1718 if (sx_paranoia_check(port, tty->name, "sx_flush_chars")) {
1719 func_exit();
1720 return;
1721 }
1722 if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
1723 !port->xmit_buf) {
1724 func_exit();
1725 return;
1726 }
1727 spin_lock_irqsave(&bp->lock, flags);
1728 port->IER |= IER_TXRDY;
1729 sx_out(port_Board(port), CD186x_CAR, port_No(port));
1730 sx_out(port_Board(port), CD186x_IER, port->IER);
1731 spin_unlock_irqrestore(&bp->lock, flags);
1732
1733 func_exit();
1734}
1735
1736
1737static int sx_write_room(struct tty_struct * tty)
1738{
1739 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1740 int ret;
1741
1742 func_enter();
1743
1744 if (sx_paranoia_check(port, tty->name, "sx_write_room")) {
1745 func_exit();
1746 return 0;
1747 }
1748
1749 ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;
1750 if (ret < 0)
1751 ret = 0;
1752
1753 func_exit();
1754 return ret;
1755}
1756
1757
1758static int sx_chars_in_buffer(struct tty_struct *tty)
1759{
1760 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1761
1762 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08001763
Linus Torvalds1da177e2005-04-16 15:20:36 -07001764 if (sx_paranoia_check(port, tty->name, "sx_chars_in_buffer")) {
1765 func_exit();
1766 return 0;
1767 }
1768 func_exit();
1769 return port->xmit_cnt;
1770}
1771
1772
1773static void sx_flush_buffer(struct tty_struct *tty)
1774{
1775 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1776 unsigned long flags;
1777 struct specialix_board * bp;
1778
1779 func_enter();
1780
1781 if (sx_paranoia_check(port, tty->name, "sx_flush_buffer")) {
1782 func_exit();
1783 return;
1784 }
1785
1786 bp = port_Board(port);
1787 spin_lock_irqsave(&port->lock, flags);
1788 port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
1789 spin_unlock_irqrestore(&port->lock, flags);
1790 tty_wakeup(tty);
1791
1792 func_exit();
1793}
1794
1795
1796static int sx_tiocmget(struct tty_struct *tty, struct file *file)
1797{
1798 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1799 struct specialix_board * bp;
1800 unsigned char status;
1801 unsigned int result;
1802 unsigned long flags;
1803
1804 func_enter();
1805
1806 if (sx_paranoia_check(port, tty->name, __FUNCTION__)) {
1807 func_exit();
1808 return -ENODEV;
1809 }
1810
1811 bp = port_Board(port);
1812 spin_lock_irqsave (&bp->lock, flags);
1813 sx_out(bp, CD186x_CAR, port_No(port));
1814 status = sx_in(bp, CD186x_MSVR);
1815 spin_unlock_irqrestore(&bp->lock, flags);
1816 dprintk (SX_DEBUG_INIT, "Got msvr[%d] = %02x, car = %d.\n",
1817 port_No(port), status, sx_in (bp, CD186x_CAR));
1818 dprintk (SX_DEBUG_INIT, "sx_port = %p, port = %p\n", sx_port, port);
1819 if (SX_CRTSCTS(port->tty)) {
Jeff Garzikd61780c2005-10-30 15:01:51 -08001820 result = /* (status & MSVR_RTS) ? */ TIOCM_DTR /* : 0) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001821 | ((status & MSVR_DTR) ? TIOCM_RTS : 0)
1822 | ((status & MSVR_CD) ? TIOCM_CAR : 0)
1823 |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */
1824 | ((status & MSVR_CTS) ? TIOCM_CTS : 0);
1825 } else {
Jeff Garzikd61780c2005-10-30 15:01:51 -08001826 result = /* (status & MSVR_RTS) ? */ TIOCM_RTS /* : 0) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001827 | ((status & MSVR_DTR) ? TIOCM_DTR : 0)
1828 | ((status & MSVR_CD) ? TIOCM_CAR : 0)
1829 |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */
1830 | ((status & MSVR_CTS) ? TIOCM_CTS : 0);
1831 }
1832
1833 func_exit();
1834
1835 return result;
1836}
1837
1838
1839static int sx_tiocmset(struct tty_struct *tty, struct file *file,
1840 unsigned int set, unsigned int clear)
1841{
1842 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1843 unsigned long flags;
1844 struct specialix_board *bp;
1845
1846 func_enter();
1847
1848 if (sx_paranoia_check(port, tty->name, __FUNCTION__)) {
1849 func_exit();
1850 return -ENODEV;
1851 }
1852
1853 bp = port_Board(port);
1854
1855 spin_lock_irqsave(&port->lock, flags);
1856 /* if (set & TIOCM_RTS)
1857 port->MSVR |= MSVR_RTS; */
1858 /* if (set & TIOCM_DTR)
1859 port->MSVR |= MSVR_DTR; */
1860
1861 if (SX_CRTSCTS(port->tty)) {
1862 if (set & TIOCM_RTS)
1863 port->MSVR |= MSVR_DTR;
1864 } else {
1865 if (set & TIOCM_DTR)
1866 port->MSVR |= MSVR_DTR;
1867 }
1868
1869 /* if (clear & TIOCM_RTS)
1870 port->MSVR &= ~MSVR_RTS; */
1871 /* if (clear & TIOCM_DTR)
1872 port->MSVR &= ~MSVR_DTR; */
1873 if (SX_CRTSCTS(port->tty)) {
1874 if (clear & TIOCM_RTS)
1875 port->MSVR &= ~MSVR_DTR;
1876 } else {
1877 if (clear & TIOCM_DTR)
1878 port->MSVR &= ~MSVR_DTR;
1879 }
1880 spin_lock_irqsave(&bp->lock, flags);
1881 sx_out(bp, CD186x_CAR, port_No(port));
1882 sx_out(bp, CD186x_MSVR, port->MSVR);
1883 spin_unlock_irqrestore(&bp->lock, flags);
1884 spin_unlock_irqrestore(&port->lock, flags);
1885 func_exit();
1886 return 0;
1887}
1888
1889
1890static inline void sx_send_break(struct specialix_port * port, unsigned long length)
1891{
1892 struct specialix_board *bp = port_Board(port);
1893 unsigned long flags;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001894
Linus Torvalds1da177e2005-04-16 15:20:36 -07001895 func_enter();
1896
1897 spin_lock_irqsave (&port->lock, flags);
1898 port->break_length = SPECIALIX_TPS / HZ * length;
1899 port->COR2 |= COR2_ETC;
1900 port->IER |= IER_TXRDY;
1901 spin_lock_irqsave(&bp->lock, flags);
1902 sx_out(bp, CD186x_CAR, port_No(port));
1903 sx_out(bp, CD186x_COR2, port->COR2);
1904 sx_out(bp, CD186x_IER, port->IER);
1905 spin_unlock_irqrestore(&bp->lock, flags);
1906 spin_unlock_irqrestore (&port->lock, flags);
1907 sx_wait_CCR(bp);
1908 spin_lock_irqsave(&bp->lock, flags);
1909 sx_out(bp, CD186x_CCR, CCR_CORCHG2);
1910 spin_unlock_irqrestore(&bp->lock, flags);
1911 sx_wait_CCR(bp);
1912
1913 func_exit();
1914}
1915
1916
1917static inline int sx_set_serial_info(struct specialix_port * port,
1918 struct serial_struct __user * newinfo)
1919{
1920 struct serial_struct tmp;
1921 struct specialix_board *bp = port_Board(port);
1922 int change_speed;
1923
1924 func_enter();
1925 /*
Jesper Juhle49332b2005-05-01 08:59:08 -07001926 if (!access_ok(VERIFY_READ, (void *) newinfo, sizeof(tmp))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001927 func_exit();
Jesper Juhle49332b2005-05-01 08:59:08 -07001928 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001929 }
1930 */
1931 if (copy_from_user(&tmp, newinfo, sizeof(tmp))) {
1932 func_enter();
1933 return -EFAULT;
1934 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001935
1936#if 0
Linus Torvalds1da177e2005-04-16 15:20:36 -07001937 if ((tmp.irq != bp->irq) ||
1938 (tmp.port != bp->base) ||
1939 (tmp.type != PORT_CIRRUS) ||
1940 (tmp.baud_base != (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC) ||
1941 (tmp.custom_divisor != 0) ||
1942 (tmp.xmit_fifo_size != CD186x_NFIFO) ||
1943 (tmp.flags & ~SPECIALIX_LEGAL_FLAGS)) {
1944 func_exit();
1945 return -EINVAL;
1946 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001947#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001948
1949 change_speed = ((port->flags & ASYNC_SPD_MASK) !=
1950 (tmp.flags & ASYNC_SPD_MASK));
1951 change_speed |= (tmp.custom_divisor != port->custom_divisor);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001952
Linus Torvalds1da177e2005-04-16 15:20:36 -07001953 if (!capable(CAP_SYS_ADMIN)) {
1954 if ((tmp.close_delay != port->close_delay) ||
1955 (tmp.closing_wait != port->closing_wait) ||
1956 ((tmp.flags & ~ASYNC_USR_MASK) !=
1957 (port->flags & ~ASYNC_USR_MASK))) {
1958 func_exit();
1959 return -EPERM;
1960 }
1961 port->flags = ((port->flags & ~ASYNC_USR_MASK) |
1962 (tmp.flags & ASYNC_USR_MASK));
1963 port->custom_divisor = tmp.custom_divisor;
1964 } else {
1965 port->flags = ((port->flags & ~ASYNC_FLAGS) |
1966 (tmp.flags & ASYNC_FLAGS));
1967 port->close_delay = tmp.close_delay;
1968 port->closing_wait = tmp.closing_wait;
1969 port->custom_divisor = tmp.custom_divisor;
1970 }
1971 if (change_speed) {
1972 sx_change_speed(bp, port);
1973 }
1974 func_exit();
1975 return 0;
1976}
1977
1978
1979static inline int sx_get_serial_info(struct specialix_port * port,
1980 struct serial_struct __user *retinfo)
1981{
1982 struct serial_struct tmp;
1983 struct specialix_board *bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001984
Linus Torvalds1da177e2005-04-16 15:20:36 -07001985 func_enter();
1986
1987 /*
Jesper Juhle49332b2005-05-01 08:59:08 -07001988 if (!access_ok(VERIFY_WRITE, (void *) retinfo, sizeof(tmp)))
1989 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001990 */
1991
1992 memset(&tmp, 0, sizeof(tmp));
1993 tmp.type = PORT_CIRRUS;
1994 tmp.line = port - sx_port;
1995 tmp.port = bp->base;
1996 tmp.irq = bp->irq;
1997 tmp.flags = port->flags;
1998 tmp.baud_base = (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC;
1999 tmp.close_delay = port->close_delay * HZ/100;
2000 tmp.closing_wait = port->closing_wait * HZ/100;
2001 tmp.custom_divisor = port->custom_divisor;
2002 tmp.xmit_fifo_size = CD186x_NFIFO;
2003 if (copy_to_user(retinfo, &tmp, sizeof(tmp))) {
2004 func_exit();
2005 return -EFAULT;
2006 }
2007
2008 func_exit();
2009 return 0;
2010}
2011
2012
Jeff Garzikd61780c2005-10-30 15:01:51 -08002013static int sx_ioctl(struct tty_struct * tty, struct file * filp,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002014 unsigned int cmd, unsigned long arg)
2015{
2016 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2017 int retval;
2018 void __user *argp = (void __user *)arg;
2019
2020 func_enter();
2021
2022 if (sx_paranoia_check(port, tty->name, "sx_ioctl")) {
2023 func_exit();
2024 return -ENODEV;
2025 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002026
Linus Torvalds1da177e2005-04-16 15:20:36 -07002027 switch (cmd) {
2028 case TCSBRK: /* SVID version: non-zero arg --> no break */
2029 retval = tty_check_change(tty);
2030 if (retval) {
2031 func_exit();
2032 return retval;
2033 }
2034 tty_wait_until_sent(tty, 0);
2035 if (!arg)
2036 sx_send_break(port, HZ/4); /* 1/4 second */
2037 return 0;
2038 case TCSBRKP: /* support for POSIX tcsendbreak() */
2039 retval = tty_check_change(tty);
2040 if (retval) {
2041 func_exit();
2042 return retval;
2043 }
2044 tty_wait_until_sent(tty, 0);
2045 sx_send_break(port, arg ? arg*(HZ/10) : HZ/4);
2046 func_exit();
2047 return 0;
2048 case TIOCGSOFTCAR:
2049 if (put_user(C_CLOCAL(tty)?1:0, (unsigned long __user *)argp)) {
2050 func_exit();
2051 return -EFAULT;
2052 }
2053 func_exit();
2054 return 0;
2055 case TIOCSSOFTCAR:
2056 if (get_user(arg, (unsigned long __user *) argp)) {
2057 func_exit();
2058 return -EFAULT;
2059 }
2060 tty->termios->c_cflag =
2061 ((tty->termios->c_cflag & ~CLOCAL) |
2062 (arg ? CLOCAL : 0));
2063 func_exit();
2064 return 0;
2065 case TIOCGSERIAL:
2066 func_exit();
2067 return sx_get_serial_info(port, argp);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002068 case TIOCSSERIAL:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002069 func_exit();
2070 return sx_set_serial_info(port, argp);
2071 default:
2072 func_exit();
2073 return -ENOIOCTLCMD;
2074 }
2075 func_exit();
2076 return 0;
2077}
2078
2079
2080static void sx_throttle(struct tty_struct * tty)
2081{
2082 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2083 struct specialix_board *bp;
2084 unsigned long flags;
2085
2086 func_enter();
2087
2088 if (sx_paranoia_check(port, tty->name, "sx_throttle")) {
2089 func_exit();
2090 return;
2091 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002092
Linus Torvalds1da177e2005-04-16 15:20:36 -07002093 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002094
Linus Torvalds1da177e2005-04-16 15:20:36 -07002095 /* Use DTR instead of RTS ! */
Jeff Garzikd61780c2005-10-30 15:01:51 -08002096 if (SX_CRTSCTS (tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002097 port->MSVR &= ~MSVR_DTR;
2098 else {
2099 /* Auch!!! I think the system shouldn't call this then. */
2100 /* Or maybe we're supposed (allowed?) to do our side of hw
Jeff Garzikd61780c2005-10-30 15:01:51 -08002101 handshake anyway, even when hardware handshake is off.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002102 When you see this in your logs, please report.... */
2103 printk (KERN_ERR "sx%d: Need to throttle, but can't (hardware hs is off)\n",
2104 port_No (port));
2105 }
2106 spin_lock_irqsave(&bp->lock, flags);
2107 sx_out(bp, CD186x_CAR, port_No(port));
2108 spin_unlock_irqrestore(&bp->lock, flags);
2109 if (I_IXOFF(tty)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002110 sx_wait_CCR(bp);
2111 spin_lock_irqsave(&bp->lock, flags);
2112 sx_out(bp, CD186x_CCR, CCR_SSCH2);
2113 spin_unlock_irqrestore(&bp->lock, flags);
2114 sx_wait_CCR(bp);
2115 }
2116 spin_lock_irqsave(&bp->lock, flags);
2117 sx_out(bp, CD186x_MSVR, port->MSVR);
2118 spin_unlock_irqrestore(&bp->lock, flags);
2119
2120 func_exit();
2121}
2122
2123
2124static void sx_unthrottle(struct tty_struct * tty)
2125{
2126 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2127 struct specialix_board *bp;
2128 unsigned long flags;
2129
2130 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002131
Linus Torvalds1da177e2005-04-16 15:20:36 -07002132 if (sx_paranoia_check(port, tty->name, "sx_unthrottle")) {
2133 func_exit();
2134 return;
2135 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002136
Linus Torvalds1da177e2005-04-16 15:20:36 -07002137 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002138
Linus Torvalds1da177e2005-04-16 15:20:36 -07002139 spin_lock_irqsave(&port->lock, flags);
2140 /* XXXX Use DTR INSTEAD???? */
2141 if (SX_CRTSCTS(tty)) {
2142 port->MSVR |= MSVR_DTR;
2143 } /* Else clause: see remark in "sx_throttle"... */
2144 spin_lock_irqsave(&bp->lock, flags);
2145 sx_out(bp, CD186x_CAR, port_No(port));
2146 spin_unlock_irqrestore(&bp->lock, flags);
2147 if (I_IXOFF(tty)) {
2148 spin_unlock_irqrestore(&port->lock, flags);
2149 sx_wait_CCR(bp);
2150 spin_lock_irqsave(&bp->lock, flags);
2151 sx_out(bp, CD186x_CCR, CCR_SSCH1);
2152 spin_unlock_irqrestore(&bp->lock, flags);
2153 sx_wait_CCR(bp);
2154 spin_lock_irqsave(&port->lock, flags);
2155 }
2156 spin_lock_irqsave(&bp->lock, flags);
2157 sx_out(bp, CD186x_MSVR, port->MSVR);
2158 spin_unlock_irqrestore(&bp->lock, flags);
2159 spin_unlock_irqrestore(&port->lock, flags);
2160
2161 func_exit();
2162}
2163
2164
2165static void sx_stop(struct tty_struct * tty)
2166{
2167 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2168 struct specialix_board *bp;
2169 unsigned long flags;
2170
2171 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002172
Linus Torvalds1da177e2005-04-16 15:20:36 -07002173 if (sx_paranoia_check(port, tty->name, "sx_stop")) {
2174 func_exit();
2175 return;
2176 }
2177
2178 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002179
Linus Torvalds1da177e2005-04-16 15:20:36 -07002180 spin_lock_irqsave(&port->lock, flags);
2181 port->IER &= ~IER_TXRDY;
2182 spin_lock_irqsave(&bp->lock, flags);
2183 sx_out(bp, CD186x_CAR, port_No(port));
2184 sx_out(bp, CD186x_IER, port->IER);
2185 spin_unlock_irqrestore(&bp->lock, flags);
2186 spin_unlock_irqrestore(&port->lock, flags);
2187
2188 func_exit();
2189}
2190
2191
2192static void sx_start(struct tty_struct * tty)
2193{
2194 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2195 struct specialix_board *bp;
2196 unsigned long flags;
2197
2198 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002199
Linus Torvalds1da177e2005-04-16 15:20:36 -07002200 if (sx_paranoia_check(port, tty->name, "sx_start")) {
2201 func_exit();
2202 return;
2203 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002204
Linus Torvalds1da177e2005-04-16 15:20:36 -07002205 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002206
Linus Torvalds1da177e2005-04-16 15:20:36 -07002207 spin_lock_irqsave(&port->lock, flags);
2208 if (port->xmit_cnt && port->xmit_buf && !(port->IER & IER_TXRDY)) {
2209 port->IER |= IER_TXRDY;
2210 spin_lock_irqsave(&bp->lock, flags);
2211 sx_out(bp, CD186x_CAR, port_No(port));
2212 sx_out(bp, CD186x_IER, port->IER);
2213 spin_unlock_irqrestore(&bp->lock, flags);
2214 }
2215 spin_unlock_irqrestore(&port->lock, flags);
2216
2217 func_exit();
2218}
2219
Linus Torvalds1da177e2005-04-16 15:20:36 -07002220static void sx_hangup(struct tty_struct * tty)
2221{
2222 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2223 struct specialix_board *bp;
2224 unsigned long flags;
2225
2226 func_enter();
2227
2228 if (sx_paranoia_check(port, tty->name, "sx_hangup")) {
2229 func_exit();
2230 return;
2231 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002232
Linus Torvalds1da177e2005-04-16 15:20:36 -07002233 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002234
Linus Torvalds1da177e2005-04-16 15:20:36 -07002235 sx_shutdown_port(bp, port);
2236 spin_lock_irqsave(&port->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002237 bp->count -= port->count;
2238 if (bp->count < 0) {
2239 printk(KERN_ERR "sx%d: sx_hangup: bad board count: %d port: %d\n",
2240 board_No(bp), bp->count, tty->index);
2241 bp->count = 0;
2242 }
2243 port->count = 0;
2244 port->flags &= ~ASYNC_NORMAL_ACTIVE;
2245 port->tty = NULL;
2246 spin_unlock_irqrestore(&port->lock, flags);
2247 wake_up_interruptible(&port->open_wait);
2248
2249 func_exit();
2250}
2251
2252
Alan Cox606d0992006-12-08 02:38:45 -08002253static void sx_set_termios(struct tty_struct * tty, struct ktermios * old_termios)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002254{
2255 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2256 unsigned long flags;
2257 struct specialix_board * bp;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002258
Linus Torvalds1da177e2005-04-16 15:20:36 -07002259 if (sx_paranoia_check(port, tty->name, "sx_set_termios"))
2260 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002261
Linus Torvalds1da177e2005-04-16 15:20:36 -07002262 if (tty->termios->c_cflag == old_termios->c_cflag &&
2263 tty->termios->c_iflag == old_termios->c_iflag)
2264 return;
2265
2266 bp = port_Board(port);
2267 spin_lock_irqsave(&port->lock, flags);
2268 sx_change_speed(port_Board(port), port);
2269 spin_unlock_irqrestore(&port->lock, flags);
2270
2271 if ((old_termios->c_cflag & CRTSCTS) &&
2272 !(tty->termios->c_cflag & CRTSCTS)) {
2273 tty->hw_stopped = 0;
2274 sx_start(tty);
2275 }
2276}
2277
Jeff Dikeb68e31d2006-10-02 02:17:18 -07002278static const struct tty_operations sx_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002279 .open = sx_open,
2280 .close = sx_close,
2281 .write = sx_write,
2282 .put_char = sx_put_char,
2283 .flush_chars = sx_flush_chars,
2284 .write_room = sx_write_room,
2285 .chars_in_buffer = sx_chars_in_buffer,
2286 .flush_buffer = sx_flush_buffer,
2287 .ioctl = sx_ioctl,
2288 .throttle = sx_throttle,
2289 .unthrottle = sx_unthrottle,
2290 .set_termios = sx_set_termios,
2291 .stop = sx_stop,
2292 .start = sx_start,
2293 .hangup = sx_hangup,
2294 .tiocmget = sx_tiocmget,
2295 .tiocmset = sx_tiocmset,
2296};
2297
2298static int sx_init_drivers(void)
2299{
2300 int error;
2301 int i;
2302
2303 func_enter();
2304
2305 specialix_driver = alloc_tty_driver(SX_NBOARD * SX_NPORT);
2306 if (!specialix_driver) {
2307 printk(KERN_ERR "sx: Couldn't allocate tty_driver.\n");
2308 func_exit();
2309 return 1;
2310 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002311
Linus Torvalds1da177e2005-04-16 15:20:36 -07002312 specialix_driver->owner = THIS_MODULE;
2313 specialix_driver->name = "ttyW";
2314 specialix_driver->major = SPECIALIX_NORMAL_MAJOR;
2315 specialix_driver->type = TTY_DRIVER_TYPE_SERIAL;
2316 specialix_driver->subtype = SERIAL_TYPE_NORMAL;
2317 specialix_driver->init_termios = tty_std_termios;
2318 specialix_driver->init_termios.c_cflag =
2319 B9600 | CS8 | CREAD | HUPCL | CLOCAL;
Alan Cox606d0992006-12-08 02:38:45 -08002320 specialix_driver->init_termios.c_ispeed = 9600;
2321 specialix_driver->init_termios.c_ospeed = 9600;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002322 specialix_driver->flags = TTY_DRIVER_REAL_RAW;
2323 tty_set_operations(specialix_driver, &sx_ops);
2324
2325 if ((error = tty_register_driver(specialix_driver))) {
2326 put_tty_driver(specialix_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002327 printk(KERN_ERR "sx: Couldn't register specialix IO8+ driver, error = %d\n",
2328 error);
2329 func_exit();
2330 return 1;
2331 }
2332 memset(sx_port, 0, sizeof(sx_port));
2333 for (i = 0; i < SX_NPORT * SX_NBOARD; i++) {
2334 sx_port[i].magic = SPECIALIX_MAGIC;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002335 sx_port[i].close_delay = 50 * HZ/100;
2336 sx_port[i].closing_wait = 3000 * HZ/100;
2337 init_waitqueue_head(&sx_port[i].open_wait);
2338 init_waitqueue_head(&sx_port[i].close_wait);
2339 spin_lock_init(&sx_port[i].lock);
2340 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002341
Linus Torvalds1da177e2005-04-16 15:20:36 -07002342 func_exit();
2343 return 0;
2344}
2345
2346static void sx_release_drivers(void)
2347{
2348 func_enter();
2349
Linus Torvalds1da177e2005-04-16 15:20:36 -07002350 tty_unregister_driver(specialix_driver);
2351 put_tty_driver(specialix_driver);
2352 func_exit();
2353}
2354
Jeff Garzikd61780c2005-10-30 15:01:51 -08002355/*
2356 * This routine must be called by kernel at boot time
Linus Torvalds1da177e2005-04-16 15:20:36 -07002357 */
2358static int __init specialix_init(void)
2359{
2360 int i;
2361 int found = 0;
2362
2363 func_enter();
2364
2365 printk(KERN_INFO "sx: Specialix IO8+ driver v" VERSION ", (c) R.E.Wolff 1997/1998.\n");
2366 printk(KERN_INFO "sx: derived from work (c) D.Gorodchanin 1994-1996.\n");
2367#ifdef CONFIG_SPECIALIX_RTSCTS
2368 printk (KERN_INFO "sx: DTR/RTS pin is always RTS.\n");
2369#else
2370 printk (KERN_INFO "sx: DTR/RTS pin is RTS when CRTSCTS is on.\n");
2371#endif
Jeff Garzikd61780c2005-10-30 15:01:51 -08002372
Linus Torvalds1da177e2005-04-16 15:20:36 -07002373 for (i = 0; i < SX_NBOARD; i++)
Ingo Molnar34af9462006-06-27 02:53:55 -07002374 spin_lock_init(&sx_board[i].lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002375
2376 if (sx_init_drivers()) {
2377 func_exit();
2378 return -EIO;
2379 }
2380
Jeff Garzikd61780c2005-10-30 15:01:51 -08002381 for (i = 0; i < SX_NBOARD; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002382 if (sx_board[i].base && !sx_probe(&sx_board[i]))
2383 found++;
2384
2385#ifdef CONFIG_PCI
2386 {
2387 struct pci_dev *pdev = NULL;
2388
2389 i=0;
2390 while (i < SX_NBOARD) {
2391 if (sx_board[i].flags & SX_BOARD_PRESENT) {
2392 i++;
2393 continue;
2394 }
Alan Cox606d0992006-12-08 02:38:45 -08002395 pdev = pci_get_device (PCI_VENDOR_ID_SPECIALIX,
Jeff Garzikd61780c2005-10-30 15:01:51 -08002396 PCI_DEVICE_ID_SPECIALIX_IO8,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002397 pdev);
2398 if (!pdev) break;
2399
2400 if (pci_enable_device(pdev))
2401 continue;
2402
2403 sx_board[i].irq = pdev->irq;
2404
2405 sx_board[i].base = pci_resource_start (pdev, 2);
2406
2407 sx_board[i].flags |= SX_BOARD_IS_PCI;
2408 if (!sx_probe(&sx_board[i]))
2409 found ++;
2410 }
Alan Cox606d0992006-12-08 02:38:45 -08002411 /* May exit pci_get sequence early with lots of boards */
2412 if (pdev != NULL)
2413 pci_dev_put(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002414 }
2415#endif
2416
2417 if (!found) {
2418 sx_release_drivers();
2419 printk(KERN_INFO "sx: No specialix IO8+ boards detected.\n");
2420 func_exit();
2421 return -EIO;
2422 }
2423
2424 func_exit();
2425 return 0;
2426}
2427
2428static int iobase[SX_NBOARD] = {0,};
2429
2430static int irq [SX_NBOARD] = {0,};
2431
2432module_param_array(iobase, int, NULL, 0);
2433module_param_array(irq, int, NULL, 0);
2434module_param(sx_debug, int, 0);
2435module_param(sx_rxfifo, int, 0);
2436#ifdef SPECIALIX_TIMER
2437module_param(sx_poll, int, 0);
2438#endif
2439
2440/*
2441 * You can setup up to 4 boards.
2442 * by specifying "iobase=0xXXX,0xXXX ..." as insmod parameter.
Jeff Garzikd61780c2005-10-30 15:01:51 -08002443 * You should specify the IRQs too in that case "irq=....,...".
2444 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07002445 * More than 4 boards in one computer is not possible, as the card can
Jeff Garzikd61780c2005-10-30 15:01:51 -08002446 * only use 4 different interrupts.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002447 *
2448 */
2449static int __init specialix_init_module(void)
2450{
2451 int i;
2452
2453 func_enter();
2454
Linus Torvalds1da177e2005-04-16 15:20:36 -07002455 if (iobase[0] || iobase[1] || iobase[2] || iobase[3]) {
2456 for(i = 0; i < SX_NBOARD; i++) {
2457 sx_board[i].base = iobase[i];
2458 sx_board[i].irq = irq[i];
2459 sx_board[i].count= 0;
2460 }
2461 }
2462
2463 func_exit();
2464
2465 return specialix_init();
2466}
Jeff Garzikd61780c2005-10-30 15:01:51 -08002467
Linus Torvalds1da177e2005-04-16 15:20:36 -07002468static void __exit specialix_exit_module(void)
2469{
2470 int i;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002471
Linus Torvalds1da177e2005-04-16 15:20:36 -07002472 func_enter();
2473
2474 sx_release_drivers();
2475 for (i = 0; i < SX_NBOARD; i++)
Jeff Garzikd61780c2005-10-30 15:01:51 -08002476 if (sx_board[i].flags & SX_BOARD_PRESENT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002477 sx_release_io_range(&sx_board[i]);
2478#ifdef SPECIALIX_TIMER
Jiri Slaby40565f12007-02-12 00:52:31 -08002479 del_timer_sync(&missed_irq_timer);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002480#endif
2481
2482 func_exit();
2483}
2484
Chuck Short76910302006-07-10 04:43:59 -07002485static struct pci_device_id specialx_pci_tbl[] __devinitdata = {
2486 { PCI_DEVICE(PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_SPECIALIX_IO8) },
2487 { }
2488};
2489MODULE_DEVICE_TABLE(pci, specialx_pci_tbl);
2490
Linus Torvalds1da177e2005-04-16 15:20:36 -07002491module_init(specialix_init_module);
2492module_exit(specialix_exit_module);
2493
2494MODULE_LICENSE("GPL");