blob: c0e08c7bca2f4ef713c966eeed532164d33282e6 [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 Garzikd61780c2005-10-30 15:01:51 -0800446 sx_interrupt (((struct specialix_board *)data)->irq,
Jiri Slabyd096f3e2007-02-12 00:52:30 -0800447 (void*)data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448 }
Jiri Slaby40565f12007-02-12 00:52:31 -0800449 mod_timer(&missed_irq_timer, jiffies + sx_poll);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450}
451#endif
452
453
454
455/* Main probing routine, also sets irq. */
456static int sx_probe(struct specialix_board *bp)
457{
458 unsigned char val1, val2;
459#if 0
460 int irqs = 0;
461 int retries;
462#endif
463 int rev;
464 int chip;
465
466 func_enter();
467
Jeff Garzikd61780c2005-10-30 15:01:51 -0800468 if (sx_request_io_range(bp)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469 func_exit();
470 return 1;
471 }
472
473 /* Are the I/O ports here ? */
474 sx_out_off(bp, CD186x_PPRL, 0x5a);
475 short_pause ();
476 val1 = sx_in_off(bp, CD186x_PPRL);
477
478 sx_out_off(bp, CD186x_PPRL, 0xa5);
479 short_pause ();
480 val2 = sx_in_off(bp, CD186x_PPRL);
481
Jeff Garzikd61780c2005-10-30 15:01:51 -0800482
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483 if ((val1 != 0x5a) || (val2 != 0xa5)) {
484 printk(KERN_INFO "sx%d: specialix IO8+ Board at 0x%03x not found.\n",
485 board_No(bp), bp->base);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800486 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487 func_exit();
488 return 1;
489 }
490
Jeff Garzikd61780c2005-10-30 15:01:51 -0800491 /* Check the DSR lines that Specialix uses as board
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492 identification */
493 val1 = read_cross_byte (bp, CD186x_MSVR, MSVR_DSR);
494 val2 = read_cross_byte (bp, CD186x_MSVR, MSVR_RTS);
495 dprintk (SX_DEBUG_INIT, "sx%d: DSR lines are: %02x, rts lines are: %02x\n",
496 board_No(bp), val1, val2);
497
498 /* They managed to switch the bit order between the docs and
499 the IO8+ card. The new PCI card now conforms to old docs.
500 They changed the PCI docs to reflect the situation on the
501 old card. */
502 val2 = (bp->flags & SX_BOARD_IS_PCI)?0x4d : 0xb2;
503 if (val1 != val2) {
504 printk(KERN_INFO "sx%d: specialix IO8+ ID %02x at 0x%03x not found (%02x).\n",
505 board_No(bp), val2, bp->base, val1);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800506 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507 func_exit();
508 return 1;
509 }
510
511
512#if 0
513 /* It's time to find IRQ for this board */
514 for (retries = 0; retries < 5 && irqs <= 0; retries++) {
515 irqs = probe_irq_on();
516 sx_init_CD186x(bp); /* Reset CD186x chip */
517 sx_out(bp, CD186x_CAR, 2); /* Select port 2 */
518 sx_wait_CCR(bp);
519 sx_out(bp, CD186x_CCR, CCR_TXEN); /* Enable transmitter */
520 sx_out(bp, CD186x_IER, IER_TXRDY); /* Enable tx empty intr */
Jiri Slaby3e98cee2007-07-17 04:05:19 -0700521 msleep(50);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522 irqs = probe_irq_off(irqs);
523
524 dprintk (SX_DEBUG_INIT, "SRSR = %02x, ", sx_in(bp, CD186x_SRSR));
525 dprintk (SX_DEBUG_INIT, "TRAR = %02x, ", sx_in(bp, CD186x_TRAR));
526 dprintk (SX_DEBUG_INIT, "GIVR = %02x, ", sx_in(bp, CD186x_GIVR));
527 dprintk (SX_DEBUG_INIT, "GICR = %02x, ", sx_in(bp, CD186x_GICR));
528 dprintk (SX_DEBUG_INIT, "\n");
529
530 /* Reset CD186x again */
531 if (!sx_init_CD186x(bp)) {
532 /* Hmmm. This is dead code anyway. */
533 }
534
535 dprintk (SX_DEBUG_INIT "val1 = %02x, val2 = %02x, val3 = %02x.\n",
Jeff Garzikd61780c2005-10-30 15:01:51 -0800536 val1, val2, val3);
537
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800539
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540#if 0
541 if (irqs <= 0) {
542 printk(KERN_ERR "sx%d: Can't find IRQ for specialix IO8+ board at 0x%03x.\n",
543 board_No(bp), bp->base);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800544 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 func_exit();
546 return 1;
547 }
548#endif
549 printk (KERN_INFO "Started with irq=%d, but now have irq=%d.\n", bp->irq, irqs);
550 if (irqs > 0)
551 bp->irq = irqs;
552#endif
553 /* Reset CD186x again */
554 if (!sx_init_CD186x(bp)) {
Jeff Garzikd61780c2005-10-30 15:01:51 -0800555 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 func_exit();
Jeff Garzikd61780c2005-10-30 15:01:51 -0800557 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558 }
559
560 sx_request_io_range(bp);
561 bp->flags |= SX_BOARD_PRESENT;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800562
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563 /* Chip revcode pkgtype
564 GFRCR SRCR bit 7
565 CD180 rev B 0x81 0
566 CD180 rev C 0x82 0
567 CD1864 rev A 0x82 1
Jeff Garzikd61780c2005-10-30 15:01:51 -0800568 CD1865 rev A 0x83 1 -- Do not use!!! Does not work.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 CD1865 rev B 0x84 1
570 -- Thanks to Gwen Wang, Cirrus Logic.
571 */
572
573 switch (sx_in_off(bp, CD186x_GFRCR)) {
574 case 0x82:chip = 1864;rev='A';break;
575 case 0x83:chip = 1865;rev='A';break;
576 case 0x84:chip = 1865;rev='B';break;
577 case 0x85:chip = 1865;rev='C';break; /* Does not exist at this time */
578 default:chip=-1;rev='x';
579 }
580
581 dprintk (SX_DEBUG_INIT, " GFCR = 0x%02x\n", sx_in_off(bp, CD186x_GFRCR) );
582
583#ifdef SPECIALIX_TIMER
Jiri Slaby40565f12007-02-12 00:52:31 -0800584 setup_timer(&missed_irq_timer, missed_irq, (unsigned long)bp);
585 mod_timer(&missed_irq_timer, jiffies + sx_poll);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586#endif
587
588 printk(KERN_INFO"sx%d: specialix IO8+ board detected at 0x%03x, IRQ %d, CD%d Rev. %c.\n",
589 board_No(bp),
590 bp->base, bp->irq,
591 chip, rev);
592
593 func_exit();
594 return 0;
595}
596
Jeff Garzikd61780c2005-10-30 15:01:51 -0800597/*
598 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599 * Interrupt processing routines.
600 * */
601
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602static inline struct specialix_port * sx_get_port(struct specialix_board * bp,
603 unsigned char const * what)
604{
605 unsigned char channel;
606 struct specialix_port * port = NULL;
607
608 channel = sx_in(bp, CD186x_GICR) >> GICR_CHAN_OFF;
609 dprintk (SX_DEBUG_CHAN, "channel: %d\n", channel);
610 if (channel < CD186x_NCH) {
611 port = &sx_port[board_No(bp) * SX_NPORT + channel];
612 dprintk (SX_DEBUG_CHAN, "port: %d %p flags: 0x%x\n",board_No(bp) * SX_NPORT + channel, port, port->flags & ASYNC_INITIALIZED);
613
614 if (port->flags & ASYNC_INITIALIZED) {
615 dprintk (SX_DEBUG_CHAN, "port: %d %p\n", channel, port);
616 func_exit();
617 return port;
618 }
619 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800620 printk(KERN_INFO "sx%d: %s interrupt from invalid port %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621 board_No(bp), what, channel);
622 return NULL;
623}
624
625
626static inline void sx_receive_exc(struct specialix_board * bp)
627{
628 struct specialix_port *port;
629 struct tty_struct *tty;
630 unsigned char status;
Alan Cox33f0f882006-01-09 20:54:13 -0800631 unsigned char ch, flag;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632
633 func_enter();
634
635 port = sx_get_port(bp, "Receive");
636 if (!port) {
637 dprintk (SX_DEBUG_RX, "Hmm, couldn't find port.\n");
638 func_exit();
639 return;
640 }
641 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800642
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643 status = sx_in(bp, CD186x_RCSR);
644
645 dprintk (SX_DEBUG_RX, "status: 0x%x\n", status);
646 if (status & RCSR_OE) {
647 port->overrun++;
648 dprintk(SX_DEBUG_FIFO, "sx%d: port %d: Overrun. Total %ld overruns.\n",
649 board_No(bp), port_No(port), port->overrun);
650 }
651 status &= port->mark_mask;
652
653 /* This flip buffer check needs to be below the reading of the
654 status register to reset the chip's IRQ.... */
Alan Cox33f0f882006-01-09 20:54:13 -0800655 if (tty_buffer_request_room(tty, 1) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656 dprintk(SX_DEBUG_FIFO, "sx%d: port %d: Working around flip buffer overflow.\n",
657 board_No(bp), port_No(port));
658 func_exit();
659 return;
660 }
661
662 ch = sx_in(bp, CD186x_RDR);
663 if (!status) {
664 func_exit();
665 return;
666 }
667 if (status & RCSR_TOUT) {
Jeff Garzikd61780c2005-10-30 15:01:51 -0800668 printk(KERN_INFO "sx%d: port %d: Receiver timeout. Hardware problems ?\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669 board_No(bp), port_No(port));
670 func_exit();
671 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800672
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673 } else if (status & RCSR_BREAK) {
674 dprintk(SX_DEBUG_RX, "sx%d: port %d: Handling break...\n",
675 board_No(bp), port_No(port));
Alan Cox33f0f882006-01-09 20:54:13 -0800676 flag = TTY_BREAK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677 if (port->flags & ASYNC_SAK)
678 do_SAK(tty);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800679
680 } else if (status & RCSR_PE)
Alan Cox33f0f882006-01-09 20:54:13 -0800681 flag = TTY_PARITY;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800682
683 else if (status & RCSR_FE)
Alan Cox33f0f882006-01-09 20:54:13 -0800684 flag = TTY_FRAME;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800685
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686 else if (status & RCSR_OE)
Alan Cox33f0f882006-01-09 20:54:13 -0800687 flag = TTY_OVERRUN;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800688
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689 else
Alan Cox33f0f882006-01-09 20:54:13 -0800690 flag = TTY_NORMAL;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800691
Alan Cox33f0f882006-01-09 20:54:13 -0800692 if(tty_insert_flip_char(tty, ch, flag))
693 tty_flip_buffer_push(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694 func_exit();
695}
696
697
698static inline void sx_receive(struct specialix_board * bp)
699{
700 struct specialix_port *port;
701 struct tty_struct *tty;
702 unsigned char count;
703
704 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -0800705
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706 if (!(port = sx_get_port(bp, "Receive"))) {
707 dprintk (SX_DEBUG_RX, "Hmm, couldn't find port.\n");
708 func_exit();
709 return;
710 }
711 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800712
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713 count = sx_in(bp, CD186x_RDCR);
714 dprintk (SX_DEBUG_RX, "port: %p: count: %d\n", port, count);
715 port->hits[count > 8 ? 9 : count]++;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800716
Alan Cox33f0f882006-01-09 20:54:13 -0800717 tty_buffer_request_room(tty, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718
Alan Cox33f0f882006-01-09 20:54:13 -0800719 while (count--)
720 tty_insert_flip_char(tty, sx_in(bp, CD186x_RDR), TTY_NORMAL);
721 tty_flip_buffer_push(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722 func_exit();
723}
724
725
726static inline void sx_transmit(struct specialix_board * bp)
727{
728 struct specialix_port *port;
729 struct tty_struct *tty;
730 unsigned char count;
731
732 func_enter();
733 if (!(port = sx_get_port(bp, "Transmit"))) {
734 func_exit();
735 return;
736 }
737 dprintk (SX_DEBUG_TX, "port: %p\n", port);
738 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800739
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740 if (port->IER & IER_TXEMPTY) {
741 /* FIFO drained */
742 sx_out(bp, CD186x_CAR, port_No(port));
743 port->IER &= ~IER_TXEMPTY;
744 sx_out(bp, CD186x_IER, port->IER);
745 func_exit();
746 return;
747 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800748
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749 if ((port->xmit_cnt <= 0 && !port->break_length)
750 || tty->stopped || tty->hw_stopped) {
751 sx_out(bp, CD186x_CAR, port_No(port));
752 port->IER &= ~IER_TXRDY;
753 sx_out(bp, CD186x_IER, port->IER);
754 func_exit();
755 return;
756 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800757
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758 if (port->break_length) {
759 if (port->break_length > 0) {
760 if (port->COR2 & COR2_ETC) {
761 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
762 sx_out(bp, CD186x_TDR, CD186x_C_SBRK);
763 port->COR2 &= ~COR2_ETC;
764 }
765 count = min_t(int, port->break_length, 0xff);
766 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
767 sx_out(bp, CD186x_TDR, CD186x_C_DELAY);
768 sx_out(bp, CD186x_TDR, count);
769 if (!(port->break_length -= count))
770 port->break_length--;
771 } else {
772 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
773 sx_out(bp, CD186x_TDR, CD186x_C_EBRK);
774 sx_out(bp, CD186x_COR2, port->COR2);
775 sx_wait_CCR(bp);
776 sx_out(bp, CD186x_CCR, CCR_CORCHG2);
777 port->break_length = 0;
778 }
779
780 func_exit();
781 return;
782 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800783
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784 count = CD186x_NFIFO;
785 do {
786 sx_out(bp, CD186x_TDR, port->xmit_buf[port->xmit_tail++]);
787 port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1);
788 if (--port->xmit_cnt <= 0)
789 break;
790 } while (--count > 0);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800791
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792 if (port->xmit_cnt <= 0) {
793 sx_out(bp, CD186x_CAR, port_No(port));
794 port->IER &= ~IER_TXRDY;
795 sx_out(bp, CD186x_IER, port->IER);
796 }
797 if (port->xmit_cnt <= port->wakeup_chars)
Jiri Slabyd0d4e1c2008-02-07 00:16:39 -0800798 tty_wakeup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799
800 func_exit();
801}
802
803
804static inline void sx_check_modem(struct specialix_board * bp)
805{
806 struct specialix_port *port;
807 struct tty_struct *tty;
808 unsigned char mcr;
809 int msvr_cd;
810
811 dprintk (SX_DEBUG_SIGNALS, "Modem intr. ");
812 if (!(port = sx_get_port(bp, "Modem")))
813 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800814
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800816
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817 mcr = sx_in(bp, CD186x_MCR);
818 printk ("mcr = %02x.\n", mcr);
819
820 if ((mcr & MCR_CDCHG)) {
821 dprintk (SX_DEBUG_SIGNALS, "CD just changed... ");
822 msvr_cd = sx_in(bp, CD186x_MSVR) & MSVR_CD;
823 if (msvr_cd) {
824 dprintk (SX_DEBUG_SIGNALS, "Waking up guys in open.\n");
825 wake_up_interruptible(&port->open_wait);
826 } else {
827 dprintk (SX_DEBUG_SIGNALS, "Sending HUP.\n");
Jiri Slabyd0d4e1c2008-02-07 00:16:39 -0800828 tty_hangup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829 }
830 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800831
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
833 if (mcr & MCR_CTSCHG) {
834 if (sx_in(bp, CD186x_MSVR) & MSVR_CTS) {
835 tty->hw_stopped = 0;
836 port->IER |= IER_TXRDY;
837 if (port->xmit_cnt <= port->wakeup_chars)
Jiri Slabyd0d4e1c2008-02-07 00:16:39 -0800838 tty_wakeup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839 } else {
840 tty->hw_stopped = 1;
841 port->IER &= ~IER_TXRDY;
842 }
843 sx_out(bp, CD186x_IER, port->IER);
844 }
845 if (mcr & MCR_DSSXHG) {
846 if (sx_in(bp, CD186x_MSVR) & MSVR_DSR) {
847 tty->hw_stopped = 0;
848 port->IER |= IER_TXRDY;
849 if (port->xmit_cnt <= port->wakeup_chars)
Jiri Slabyd0d4e1c2008-02-07 00:16:39 -0800850 tty_wakeup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851 } else {
852 tty->hw_stopped = 1;
853 port->IER &= ~IER_TXRDY;
854 }
855 sx_out(bp, CD186x_IER, port->IER);
856 }
857#endif /* SPECIALIX_BRAIN_DAMAGED_CTS */
Jeff Garzikd61780c2005-10-30 15:01:51 -0800858
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859 /* Clear change bits */
860 sx_out(bp, CD186x_MCR, 0);
861}
862
863
864/* The main interrupt processing routine */
David Howells7d12e782006-10-05 14:55:46 +0100865static irqreturn_t sx_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866{
867 unsigned char status;
868 unsigned char ack;
869 struct specialix_board *bp;
870 unsigned long loop = 0;
871 int saved_reg;
872 unsigned long flags;
873
874 func_enter();
875
876 bp = dev_id;
877 spin_lock_irqsave(&bp->lock, flags);
878
879 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 -0400880 if (!(bp->flags & SX_BOARD_ACTIVE)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881 dprintk (SX_DEBUG_IRQ, "sx: False interrupt. irq %d.\n", irq);
882 spin_unlock_irqrestore(&bp->lock, flags);
883 func_exit();
884 return IRQ_NONE;
885 }
886
887 saved_reg = bp->reg;
888
889 while ((++loop < 16) && (status = (sx_in(bp, CD186x_SRSR) &
890 (SRSR_RREQint |
891 SRSR_TREQint |
Jeff Garzikd61780c2005-10-30 15:01:51 -0800892 SRSR_MREQint)))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700893 if (status & SRSR_RREQint) {
894 ack = sx_in(bp, CD186x_RRAR);
895
896 if (ack == (SX_ID | GIVR_IT_RCV))
897 sx_receive(bp);
898 else if (ack == (SX_ID | GIVR_IT_REXC))
899 sx_receive_exc(bp);
900 else
901 printk(KERN_ERR "sx%d: status: 0x%x Bad receive ack 0x%02x.\n",
902 board_No(bp), status, ack);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800903
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904 } else if (status & SRSR_TREQint) {
905 ack = sx_in(bp, CD186x_TRAR);
906
907 if (ack == (SX_ID | GIVR_IT_TX))
908 sx_transmit(bp);
909 else
910 printk(KERN_ERR "sx%d: status: 0x%x Bad transmit ack 0x%02x. port: %d\n",
911 board_No(bp), status, ack, port_No (sx_get_port (bp, "Int")));
912 } else if (status & SRSR_MREQint) {
913 ack = sx_in(bp, CD186x_MRAR);
914
Jeff Garzikd61780c2005-10-30 15:01:51 -0800915 if (ack == (SX_ID | GIVR_IT_MODEM))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916 sx_check_modem(bp);
917 else
918 printk(KERN_ERR "sx%d: status: 0x%x Bad modem ack 0x%02x.\n",
919 board_No(bp), status, ack);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800920
921 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922
923 sx_out(bp, CD186x_EOIR, 0); /* Mark end of interrupt */
924 }
925 bp->reg = saved_reg;
926 outb (bp->reg, bp->base + SX_ADDR_REG);
927 spin_unlock_irqrestore(&bp->lock, flags);
928 func_exit();
929 return IRQ_HANDLED;
930}
931
932
933/*
934 * Routines for open & close processing.
935 */
936
937static void turn_ints_off (struct specialix_board *bp)
938{
939 unsigned long flags;
940
941 func_enter();
942 if (bp->flags & SX_BOARD_IS_PCI) {
943 /* This was intended for enabeling the interrupt on the
944 * PCI card. However it seems that it's already enabled
945 * and as PCI interrupts can be shared, there is no real
946 * reason to have to turn it off. */
947 }
948
949 spin_lock_irqsave(&bp->lock, flags);
950 (void) sx_in_off (bp, 0); /* Turn off interrupts. */
951 spin_unlock_irqrestore(&bp->lock, flags);
952
953 func_exit();
954}
955
956static void turn_ints_on (struct specialix_board *bp)
957{
958 unsigned long flags;
959
960 func_enter();
961
962 if (bp->flags & SX_BOARD_IS_PCI) {
963 /* play with the PCI chip. See comment above. */
964 }
965 spin_lock_irqsave(&bp->lock, flags);
966 (void) sx_in (bp, 0); /* Turn ON interrupts. */
967 spin_unlock_irqrestore(&bp->lock, flags);
968
969 func_exit();
970}
971
972
973/* Called with disabled interrupts */
974static inline int sx_setup_board(struct specialix_board * bp)
975{
976 int error;
977
Jeff Garzikd61780c2005-10-30 15:01:51 -0800978 if (bp->flags & SX_BOARD_ACTIVE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979 return 0;
980
981 if (bp->flags & SX_BOARD_IS_PCI)
Thomas Gleixner0f2ed4c2006-07-01 19:29:33 -0700982 error = request_irq(bp->irq, sx_interrupt, IRQF_DISABLED | IRQF_SHARED, "specialix IO8+", bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983 else
Thomas Gleixner0f2ed4c2006-07-01 19:29:33 -0700984 error = request_irq(bp->irq, sx_interrupt, IRQF_DISABLED, "specialix IO8+", bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985
Jeff Garzikd61780c2005-10-30 15:01:51 -0800986 if (error)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987 return error;
988
989 turn_ints_on (bp);
990 bp->flags |= SX_BOARD_ACTIVE;
991
992 return 0;
993}
994
995
996/* Called with disabled interrupts */
997static inline void sx_shutdown_board(struct specialix_board *bp)
998{
999 func_enter();
1000
1001 if (!(bp->flags & SX_BOARD_ACTIVE)) {
1002 func_exit();
1003 return;
1004 }
1005
1006 bp->flags &= ~SX_BOARD_ACTIVE;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001007
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008 dprintk (SX_DEBUG_IRQ, "Freeing IRQ%d for board %d.\n",
1009 bp->irq, board_No (bp));
1010 free_irq(bp->irq, bp);
1011
1012 turn_ints_off (bp);
1013
1014
1015 func_exit();
1016}
1017
1018
1019/*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001020 * Setting up port characteristics.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021 * Must be called with disabled interrupts
1022 */
1023static void sx_change_speed(struct specialix_board *bp, struct specialix_port *port)
1024{
1025 struct tty_struct *tty;
1026 unsigned long baud;
1027 long tmp;
1028 unsigned char cor1 = 0, cor3 = 0;
1029 unsigned char mcor1 = 0, mcor2 = 0;
1030 static unsigned long again;
1031 unsigned long flags;
1032
1033 func_enter();
1034
1035 if (!(tty = port->tty) || !tty->termios) {
1036 func_exit();
1037 return;
1038 }
1039
1040 port->IER = 0;
1041 port->COR2 = 0;
1042 /* Select port on the board */
1043 spin_lock_irqsave(&bp->lock, flags);
1044 sx_out(bp, CD186x_CAR, port_No(port));
1045
1046 /* The Specialix board doens't implement the RTS lines.
1047 They are used to set the IRQ level. Don't touch them. */
1048 if (SX_CRTSCTS(tty))
1049 port->MSVR = MSVR_DTR | (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
1050 else
1051 port->MSVR = (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
1052 spin_unlock_irqrestore(&bp->lock, flags);
1053 dprintk (SX_DEBUG_TERMIOS, "sx: got MSVR=%02x.\n", port->MSVR);
Alan Cox67cc0162006-09-29 02:01:39 -07001054 baud = tty_get_baud_rate(tty);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001055
Alan Cox67cc0162006-09-29 02:01:39 -07001056 if (baud == 38400) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057 if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
Adrian Bunka4bb2cf2006-10-17 00:10:00 -07001058 baud = 57600;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059 if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
Adrian Bunka4bb2cf2006-10-17 00:10:00 -07001060 baud = 115200;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001062
Alan Cox67cc0162006-09-29 02:01:39 -07001063 if (!baud) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064 /* Drop DTR & exit */
1065 dprintk (SX_DEBUG_TERMIOS, "Dropping DTR... Hmm....\n");
1066 if (!SX_CRTSCTS (tty)) {
1067 port -> MSVR &= ~ MSVR_DTR;
1068 spin_lock_irqsave(&bp->lock, flags);
1069 sx_out(bp, CD186x_MSVR, port->MSVR );
1070 spin_unlock_irqrestore(&bp->lock, flags);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001071 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072 else
1073 dprintk (SX_DEBUG_TERMIOS, "Can't drop DTR: no DTR.\n");
1074 return;
1075 } else {
1076 /* Set DTR on */
1077 if (!SX_CRTSCTS (tty)) {
1078 port ->MSVR |= MSVR_DTR;
1079 }
1080 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001081
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 /*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001083 * Now we must calculate some speed depended things
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084 */
1085
1086 /* Set baud rate for port */
1087 tmp = port->custom_divisor ;
1088 if ( tmp )
1089 printk (KERN_INFO "sx%d: Using custom baud rate divisor %ld. \n"
1090 "This is an untested option, please be carefull.\n",
1091 port_No (port), tmp);
1092 else
Alan Cox67cc0162006-09-29 02:01:39 -07001093 tmp = (((SX_OSCFREQ + baud/2) / baud +
Linus Torvalds1da177e2005-04-16 15:20:36 -07001094 CD186x_TPC/2) / CD186x_TPC);
1095
Jeff Garzikd61780c2005-10-30 15:01:51 -08001096 if ((tmp < 0x10) && time_before(again, jiffies)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001097 again = jiffies + HZ * 60;
1098 /* Page 48 of version 2.0 of the CL-CD1865 databook */
1099 if (tmp >= 12) {
1100 printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
1101 "Performance degradation is possible.\n"
1102 "Read specialix.txt for more info.\n",
1103 port_No (port), tmp);
1104 } else {
1105 printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
1106 "Warning: overstressing Cirrus chip. "
1107 "This might not work.\n"
Jeff Garzikd61780c2005-10-30 15:01:51 -08001108 "Read specialix.txt for more info.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001109 port_No (port), tmp);
1110 }
1111 }
1112 spin_lock_irqsave(&bp->lock, flags);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001113 sx_out(bp, CD186x_RBPRH, (tmp >> 8) & 0xff);
1114 sx_out(bp, CD186x_TBPRH, (tmp >> 8) & 0xff);
1115 sx_out(bp, CD186x_RBPRL, tmp & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116 sx_out(bp, CD186x_TBPRL, tmp & 0xff);
1117 spin_unlock_irqrestore(&bp->lock, flags);
Adrian Bunka4bb2cf2006-10-17 00:10:00 -07001118 if (port->custom_divisor)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001119 baud = (SX_OSCFREQ + port->custom_divisor/2) / port->custom_divisor;
Adrian Bunka4bb2cf2006-10-17 00:10:00 -07001120 baud = (baud + 5) / 10; /* Estimated CPS */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001121
1122 /* Two timer ticks seems enough to wakeup something like SLIP driver */
Jeff Garzikd61780c2005-10-30 15:01:51 -08001123 tmp = ((baud + HZ/2) / HZ) * 2 - CD186x_NFIFO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001124 port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ?
1125 SERIAL_XMIT_SIZE - 1 : tmp);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001126
Linus Torvalds1da177e2005-04-16 15:20:36 -07001127 /* Receiver timeout will be transmission time for 1.5 chars */
1128 tmp = (SPECIALIX_TPS + SPECIALIX_TPS/2 + baud/2) / baud;
1129 tmp = (tmp > 0xff) ? 0xff : tmp;
1130 spin_lock_irqsave(&bp->lock, flags);
1131 sx_out(bp, CD186x_RTPR, tmp);
1132 spin_unlock_irqrestore(&bp->lock, flags);
1133 switch (C_CSIZE(tty)) {
1134 case CS5:
1135 cor1 |= COR1_5BITS;
1136 break;
1137 case CS6:
1138 cor1 |= COR1_6BITS;
1139 break;
1140 case CS7:
1141 cor1 |= COR1_7BITS;
1142 break;
1143 case CS8:
1144 cor1 |= COR1_8BITS;
1145 break;
1146 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001147
1148 if (C_CSTOPB(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 cor1 |= COR1_2SB;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001150
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151 cor1 |= COR1_IGNORE;
1152 if (C_PARENB(tty)) {
1153 cor1 |= COR1_NORMPAR;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001154 if (C_PARODD(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155 cor1 |= COR1_ODDP;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001156 if (I_INPCK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001157 cor1 &= ~COR1_IGNORE;
1158 }
1159 /* Set marking of some errors */
1160 port->mark_mask = RCSR_OE | RCSR_TOUT;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001161 if (I_INPCK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 port->mark_mask |= RCSR_FE | RCSR_PE;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001163 if (I_BRKINT(tty) || I_PARMRK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 port->mark_mask |= RCSR_BREAK;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001165 if (I_IGNPAR(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166 port->mark_mask &= ~(RCSR_FE | RCSR_PE);
1167 if (I_IGNBRK(tty)) {
1168 port->mark_mask &= ~RCSR_BREAK;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001169 if (I_IGNPAR(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001170 /* Real raw mode. Ignore all */
1171 port->mark_mask &= ~RCSR_OE;
1172 }
1173 /* Enable Hardware Flow Control */
1174 if (C_CRTSCTS(tty)) {
1175#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
1176 port->IER |= IER_DSR | IER_CTS;
1177 mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD;
1178 mcor2 |= MCOR2_DSROD | MCOR2_CTSOD;
1179 spin_lock_irqsave(&bp->lock, flags);
1180 tty->hw_stopped = !(sx_in(bp, CD186x_MSVR) & (MSVR_CTS|MSVR_DSR));
1181 spin_unlock_irqrestore(&bp->lock, flags);
1182#else
Jeff Garzikd61780c2005-10-30 15:01:51 -08001183 port->COR2 |= COR2_CTSAE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001184#endif
1185 }
1186 /* Enable Software Flow Control. FIXME: I'm not sure about this */
1187 /* Some people reported that it works, but I still doubt it */
1188 if (I_IXON(tty)) {
1189 port->COR2 |= COR2_TXIBE;
1190 cor3 |= (COR3_FCT | COR3_SCDE);
1191 if (I_IXANY(tty))
1192 port->COR2 |= COR2_IXM;
1193 spin_lock_irqsave(&bp->lock, flags);
1194 sx_out(bp, CD186x_SCHR1, START_CHAR(tty));
1195 sx_out(bp, CD186x_SCHR2, STOP_CHAR(tty));
1196 sx_out(bp, CD186x_SCHR3, START_CHAR(tty));
1197 sx_out(bp, CD186x_SCHR4, STOP_CHAR(tty));
1198 spin_unlock_irqrestore(&bp->lock, flags);
1199 }
1200 if (!C_CLOCAL(tty)) {
1201 /* Enable CD check */
1202 port->IER |= IER_CD;
1203 mcor1 |= MCOR1_CDZD;
1204 mcor2 |= MCOR2_CDOD;
1205 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001206
1207 if (C_CREAD(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208 /* Enable receiver */
1209 port->IER |= IER_RXD;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001210
Linus Torvalds1da177e2005-04-16 15:20:36 -07001211 /* Set input FIFO size (1-8 bytes) */
1212 cor3 |= sx_rxfifo;
1213 /* Setting up CD186x channel registers */
1214 spin_lock_irqsave(&bp->lock, flags);
1215 sx_out(bp, CD186x_COR1, cor1);
1216 sx_out(bp, CD186x_COR2, port->COR2);
1217 sx_out(bp, CD186x_COR3, cor3);
1218 spin_unlock_irqrestore(&bp->lock, flags);
1219 /* Make CD186x know about registers change */
1220 sx_wait_CCR(bp);
1221 spin_lock_irqsave(&bp->lock, flags);
1222 sx_out(bp, CD186x_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3);
1223 /* Setting up modem option registers */
1224 dprintk (SX_DEBUG_TERMIOS, "Mcor1 = %02x, mcor2 = %02x.\n", mcor1, mcor2);
1225 sx_out(bp, CD186x_MCOR1, mcor1);
1226 sx_out(bp, CD186x_MCOR2, mcor2);
1227 spin_unlock_irqrestore(&bp->lock, flags);
1228 /* Enable CD186x transmitter & receiver */
1229 sx_wait_CCR(bp);
1230 spin_lock_irqsave(&bp->lock, flags);
1231 sx_out(bp, CD186x_CCR, CCR_TXEN | CCR_RXEN);
1232 /* Enable interrupts */
1233 sx_out(bp, CD186x_IER, port->IER);
1234 /* And finally set the modem lines... */
1235 sx_out(bp, CD186x_MSVR, port->MSVR);
1236 spin_unlock_irqrestore(&bp->lock, flags);
1237
1238 func_exit();
1239}
1240
1241
1242/* Must be called with interrupts enabled */
1243static int sx_setup_port(struct specialix_board *bp, struct specialix_port *port)
1244{
1245 unsigned long flags;
1246
1247 func_enter();
1248
1249 if (port->flags & ASYNC_INITIALIZED) {
1250 func_exit();
1251 return 0;
1252 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001253
Linus Torvalds1da177e2005-04-16 15:20:36 -07001254 if (!port->xmit_buf) {
1255 /* We may sleep in get_zeroed_page() */
1256 unsigned long tmp;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001257
Linus Torvalds1da177e2005-04-16 15:20:36 -07001258 if (!(tmp = get_zeroed_page(GFP_KERNEL))) {
1259 func_exit();
1260 return -ENOMEM;
1261 }
1262
1263 if (port->xmit_buf) {
1264 free_page(tmp);
1265 func_exit();
1266 return -ERESTARTSYS;
1267 }
1268 port->xmit_buf = (unsigned char *) tmp;
1269 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001270
Linus Torvalds1da177e2005-04-16 15:20:36 -07001271 spin_lock_irqsave(&port->lock, flags);
1272
Jeff Garzikd61780c2005-10-30 15:01:51 -08001273 if (port->tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274 clear_bit(TTY_IO_ERROR, &port->tty->flags);
1275
1276 port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
1277 sx_change_speed(bp, port);
1278 port->flags |= ASYNC_INITIALIZED;
1279
1280 spin_unlock_irqrestore(&port->lock, flags);
1281
Jeff Garzikd61780c2005-10-30 15:01:51 -08001282
Linus Torvalds1da177e2005-04-16 15:20:36 -07001283 func_exit();
1284 return 0;
1285}
1286
1287
1288/* Must be called with interrupts disabled */
1289static void sx_shutdown_port(struct specialix_board *bp, struct specialix_port *port)
1290{
1291 struct tty_struct *tty;
1292 int i;
1293 unsigned long flags;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001294
Linus Torvalds1da177e2005-04-16 15:20:36 -07001295 func_enter();
1296
1297 if (!(port->flags & ASYNC_INITIALIZED)) {
1298 func_exit();
1299 return;
1300 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001301
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302 if (sx_debug & SX_DEBUG_FIFO) {
1303 dprintk(SX_DEBUG_FIFO, "sx%d: port %d: %ld overruns, FIFO hits [ ",
1304 board_No(bp), port_No(port), port->overrun);
1305 for (i = 0; i < 10; i++) {
1306 dprintk(SX_DEBUG_FIFO, "%ld ", port->hits[i]);
1307 }
1308 dprintk(SX_DEBUG_FIFO, "].\n");
1309 }
1310
1311 if (port->xmit_buf) {
1312 free_page((unsigned long) port->xmit_buf);
1313 port->xmit_buf = NULL;
1314 }
1315
1316 /* Select port */
1317 spin_lock_irqsave(&bp->lock, flags);
1318 sx_out(bp, CD186x_CAR, port_No(port));
1319
1320 if (!(tty = port->tty) || C_HUPCL(tty)) {
1321 /* Drop DTR */
1322 sx_out(bp, CD186x_MSVDTR, 0);
1323 }
1324 spin_unlock_irqrestore(&bp->lock, flags);
1325 /* Reset port */
1326 sx_wait_CCR(bp);
1327 spin_lock_irqsave(&bp->lock, flags);
1328 sx_out(bp, CD186x_CCR, CCR_SOFTRESET);
1329 /* Disable all interrupts from this port */
1330 port->IER = 0;
1331 sx_out(bp, CD186x_IER, port->IER);
1332 spin_unlock_irqrestore(&bp->lock, flags);
1333 if (tty)
1334 set_bit(TTY_IO_ERROR, &tty->flags);
1335 port->flags &= ~ASYNC_INITIALIZED;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001336
1337 if (!bp->count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001338 sx_shutdown_board(bp);
1339 func_exit();
1340}
1341
Jeff Garzikd61780c2005-10-30 15:01:51 -08001342
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343static int block_til_ready(struct tty_struct *tty, struct file * filp,
1344 struct specialix_port *port)
1345{
1346 DECLARE_WAITQUEUE(wait, current);
1347 struct specialix_board *bp = port_Board(port);
1348 int retval;
1349 int do_clocal = 0;
1350 int CD;
1351 unsigned long flags;
1352
1353 func_enter();
1354
1355 /*
1356 * If the device is in the middle of being closed, then block
1357 * until it's done, and then try again.
1358 */
1359 if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {
1360 interruptible_sleep_on(&port->close_wait);
1361 if (port->flags & ASYNC_HUP_NOTIFY) {
1362 func_exit();
1363 return -EAGAIN;
1364 } else {
1365 func_exit();
1366 return -ERESTARTSYS;
1367 }
1368 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001369
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370 /*
1371 * If non-blocking mode is set, or the port is not enabled,
1372 * then make the check up front and then exit.
1373 */
1374 if ((filp->f_flags & O_NONBLOCK) ||
1375 (tty->flags & (1 << TTY_IO_ERROR))) {
1376 port->flags |= ASYNC_NORMAL_ACTIVE;
1377 func_exit();
1378 return 0;
1379 }
1380
1381 if (C_CLOCAL(tty))
1382 do_clocal = 1;
1383
1384 /*
1385 * Block waiting for the carrier detect and the line to become
1386 * free (i.e., not in use by the callout). While we are in
1387 * this loop, info->count is dropped by one, so that
1388 * rs_close() knows when to free things. We restore it upon
1389 * exit, either normal or abnormal.
1390 */
1391 retval = 0;
1392 add_wait_queue(&port->open_wait, &wait);
1393 spin_lock_irqsave(&port->lock, flags);
1394 if (!tty_hung_up_p(filp)) {
1395 port->count--;
1396 }
1397 spin_unlock_irqrestore(&port->lock, flags);
1398 port->blocked_open++;
1399 while (1) {
1400 spin_lock_irqsave(&bp->lock, flags);
1401 sx_out(bp, CD186x_CAR, port_No(port));
1402 CD = sx_in(bp, CD186x_MSVR) & MSVR_CD;
1403 if (SX_CRTSCTS (tty)) {
1404 /* Activate RTS */
1405 port->MSVR |= MSVR_DTR; /* WTF? */
1406 sx_out (bp, CD186x_MSVR, port->MSVR);
1407 } else {
1408 /* Activate DTR */
1409 port->MSVR |= MSVR_DTR;
1410 sx_out (bp, CD186x_MSVR, port->MSVR);
1411 }
1412 spin_unlock_irqrestore(&bp->lock, flags);
1413 set_current_state(TASK_INTERRUPTIBLE);
1414 if (tty_hung_up_p(filp) ||
1415 !(port->flags & ASYNC_INITIALIZED)) {
1416 if (port->flags & ASYNC_HUP_NOTIFY)
1417 retval = -EAGAIN;
1418 else
Jeff Garzikd61780c2005-10-30 15:01:51 -08001419 retval = -ERESTARTSYS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001420 break;
1421 }
1422 if (!(port->flags & ASYNC_CLOSING) &&
1423 (do_clocal || CD))
1424 break;
1425 if (signal_pending(current)) {
1426 retval = -ERESTARTSYS;
1427 break;
1428 }
1429 schedule();
1430 }
1431
1432 set_current_state(TASK_RUNNING);
1433 remove_wait_queue(&port->open_wait, &wait);
1434 spin_lock_irqsave(&port->lock, flags);
1435 if (!tty_hung_up_p(filp)) {
1436 port->count++;
1437 }
1438 port->blocked_open--;
1439 spin_unlock_irqrestore(&port->lock, flags);
1440 if (retval) {
1441 func_exit();
1442 return retval;
1443 }
1444
1445 port->flags |= ASYNC_NORMAL_ACTIVE;
1446 func_exit();
1447 return 0;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001448}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001449
1450
1451static int sx_open(struct tty_struct * tty, struct file * filp)
1452{
1453 int board;
1454 int error;
1455 struct specialix_port * port;
1456 struct specialix_board * bp;
1457 int i;
1458 unsigned long flags;
1459
1460 func_enter();
1461
1462 board = SX_BOARD(tty->index);
1463
1464 if (board >= SX_NBOARD || !(sx_board[board].flags & SX_BOARD_PRESENT)) {
1465 func_exit();
1466 return -ENODEV;
1467 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001468
Linus Torvalds1da177e2005-04-16 15:20:36 -07001469 bp = &sx_board[board];
1470 port = sx_port + board * SX_NPORT + SX_PORT(tty->index);
1471 port->overrun = 0;
1472 for (i = 0; i < 10; i++)
1473 port->hits[i]=0;
1474
1475 dprintk (SX_DEBUG_OPEN, "Board = %d, bp = %p, port = %p, portno = %d.\n",
1476 board, bp, port, SX_PORT(tty->index));
1477
1478 if (sx_paranoia_check(port, tty->name, "sx_open")) {
1479 func_enter();
1480 return -ENODEV;
1481 }
1482
1483 if ((error = sx_setup_board(bp))) {
1484 func_exit();
1485 return error;
1486 }
1487
1488 spin_lock_irqsave(&bp->lock, flags);
1489 port->count++;
1490 bp->count++;
1491 tty->driver_data = port;
1492 port->tty = tty;
1493 spin_unlock_irqrestore(&bp->lock, flags);
1494
1495 if ((error = sx_setup_port(bp, port))) {
1496 func_enter();
1497 return error;
1498 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001499
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500 if ((error = block_til_ready(tty, filp, port))) {
1501 func_enter();
1502 return error;
1503 }
1504
1505 func_exit();
1506 return 0;
1507}
1508
1509
1510static void sx_close(struct tty_struct * tty, struct file * filp)
1511{
1512 struct specialix_port *port = (struct specialix_port *) tty->driver_data;
1513 struct specialix_board *bp;
1514 unsigned long flags;
1515 unsigned long timeout;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001516
Linus Torvalds1da177e2005-04-16 15:20:36 -07001517 func_enter();
1518 if (!port || sx_paranoia_check(port, tty->name, "close")) {
1519 func_exit();
1520 return;
1521 }
1522 spin_lock_irqsave(&port->lock, flags);
1523
1524 if (tty_hung_up_p(filp)) {
1525 spin_unlock_irqrestore(&port->lock, flags);
1526 func_exit();
1527 return;
1528 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001529
Linus Torvalds1da177e2005-04-16 15:20:36 -07001530 bp = port_Board(port);
1531 if ((tty->count == 1) && (port->count != 1)) {
1532 printk(KERN_ERR "sx%d: sx_close: bad port count;"
1533 " tty->count is 1, port count is %d\n",
1534 board_No(bp), port->count);
1535 port->count = 1;
1536 }
1537
1538 if (port->count > 1) {
1539 port->count--;
1540 bp->count--;
1541
1542 spin_unlock_irqrestore(&port->lock, flags);
1543
1544 func_exit();
1545 return;
1546 }
1547 port->flags |= ASYNC_CLOSING;
1548 /*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001549 * Now we wait for the transmit buffer to clear; and we notify
Linus Torvalds1da177e2005-04-16 15:20:36 -07001550 * the line discipline to only process XON/XOFF characters.
1551 */
1552 tty->closing = 1;
1553 spin_unlock_irqrestore(&port->lock, flags);
1554 dprintk (SX_DEBUG_OPEN, "Closing\n");
1555 if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) {
1556 tty_wait_until_sent(tty, port->closing_wait);
1557 }
1558 /*
1559 * At this point we stop accepting input. To do this, we
1560 * disable the receive line status interrupts, and tell the
1561 * interrupt driver to stop checking the data ready bit in the
1562 * line status register.
1563 */
1564 dprintk (SX_DEBUG_OPEN, "Closed\n");
1565 port->IER &= ~IER_RXD;
1566 if (port->flags & ASYNC_INITIALIZED) {
1567 port->IER &= ~IER_TXRDY;
1568 port->IER |= IER_TXEMPTY;
1569 spin_lock_irqsave(&bp->lock, flags);
1570 sx_out(bp, CD186x_CAR, port_No(port));
1571 sx_out(bp, CD186x_IER, port->IER);
1572 spin_unlock_irqrestore(&bp->lock, flags);
1573 /*
1574 * Before we drop DTR, make sure the UART transmitter
1575 * has completely drained; this is especially
1576 * important if there is a transmit FIFO!
1577 */
1578 timeout = jiffies+HZ;
1579 while(port->IER & IER_TXEMPTY) {
1580 set_current_state (TASK_INTERRUPTIBLE);
1581 msleep_interruptible(jiffies_to_msecs(port->timeout));
1582 if (time_after(jiffies, timeout)) {
1583 printk (KERN_INFO "Timeout waiting for close\n");
1584 break;
1585 }
1586 }
1587
1588 }
1589
1590 if (--bp->count < 0) {
1591 printk(KERN_ERR "sx%d: sx_shutdown_port: bad board count: %d port: %d\n",
1592 board_No(bp), bp->count, tty->index);
1593 bp->count = 0;
1594 }
1595 if (--port->count < 0) {
1596 printk(KERN_ERR "sx%d: sx_close: bad port count for tty%d: %d\n",
1597 board_No(bp), port_No(port), port->count);
1598 port->count = 0;
1599 }
1600
1601 sx_shutdown_port(bp, port);
1602 if (tty->driver->flush_buffer)
1603 tty->driver->flush_buffer(tty);
1604 tty_ldisc_flush(tty);
1605 spin_lock_irqsave(&port->lock, flags);
1606 tty->closing = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001607 port->tty = NULL;
1608 spin_unlock_irqrestore(&port->lock, flags);
1609 if (port->blocked_open) {
1610 if (port->close_delay) {
1611 msleep_interruptible(jiffies_to_msecs(port->close_delay));
1612 }
1613 wake_up_interruptible(&port->open_wait);
1614 }
1615 port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
1616 wake_up_interruptible(&port->close_wait);
1617
1618 func_exit();
1619}
1620
1621
Jeff Garzikd61780c2005-10-30 15:01:51 -08001622static int sx_write(struct tty_struct * tty,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001623 const unsigned char *buf, int count)
1624{
1625 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1626 struct specialix_board *bp;
1627 int c, total = 0;
1628 unsigned long flags;
1629
1630 func_enter();
1631 if (sx_paranoia_check(port, tty->name, "sx_write")) {
1632 func_exit();
1633 return 0;
1634 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001635
Linus Torvalds1da177e2005-04-16 15:20:36 -07001636 bp = port_Board(port);
1637
Jiri Slaby365e0222006-09-30 23:28:11 -07001638 if (!port->xmit_buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001639 func_exit();
1640 return 0;
1641 }
1642
1643 while (1) {
1644 spin_lock_irqsave(&port->lock, flags);
1645 c = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
1646 SERIAL_XMIT_SIZE - port->xmit_head));
1647 if (c <= 0) {
1648 spin_unlock_irqrestore(&port->lock, flags);
1649 break;
1650 }
1651 memcpy(port->xmit_buf + port->xmit_head, buf, c);
1652 port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
1653 port->xmit_cnt += c;
1654 spin_unlock_irqrestore(&port->lock, flags);
1655
1656 buf += c;
1657 count -= c;
1658 total += c;
1659 }
1660
1661 spin_lock_irqsave(&bp->lock, flags);
1662 if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
1663 !(port->IER & IER_TXRDY)) {
1664 port->IER |= IER_TXRDY;
1665 sx_out(bp, CD186x_CAR, port_No(port));
1666 sx_out(bp, CD186x_IER, port->IER);
1667 }
1668 spin_unlock_irqrestore(&bp->lock, flags);
1669 func_exit();
1670
1671 return total;
1672}
1673
1674
1675static void sx_put_char(struct tty_struct * tty, unsigned char ch)
1676{
1677 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1678 unsigned long flags;
1679 struct specialix_board * bp;
1680
1681 func_enter();
1682
1683 if (sx_paranoia_check(port, tty->name, "sx_put_char")) {
1684 func_exit();
1685 return;
1686 }
1687 dprintk (SX_DEBUG_TX, "check tty: %p %p\n", tty, port->xmit_buf);
Eric Sesterhenn326f28e92006-06-25 05:48:48 -07001688 if (!port->xmit_buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001689 func_exit();
1690 return;
1691 }
1692 bp = port_Board(port);
1693 spin_lock_irqsave(&port->lock, flags);
1694
1695 dprintk (SX_DEBUG_TX, "xmit_cnt: %d xmit_buf: %p\n", port->xmit_cnt, port->xmit_buf);
1696 if ((port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) || (!port->xmit_buf)) {
1697 spin_unlock_irqrestore(&port->lock, flags);
1698 dprintk (SX_DEBUG_TX, "Exit size\n");
1699 func_exit();
1700 return;
1701 }
1702 dprintk (SX_DEBUG_TX, "Handle xmit: %p %p\n", port, port->xmit_buf);
1703 port->xmit_buf[port->xmit_head++] = ch;
1704 port->xmit_head &= SERIAL_XMIT_SIZE - 1;
1705 port->xmit_cnt++;
1706 spin_unlock_irqrestore(&port->lock, flags);
1707
1708 func_exit();
1709}
1710
1711
1712static void sx_flush_chars(struct tty_struct * tty)
1713{
1714 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1715 unsigned long flags;
1716 struct specialix_board * bp = port_Board(port);
1717
1718 func_enter();
1719
1720 if (sx_paranoia_check(port, tty->name, "sx_flush_chars")) {
1721 func_exit();
1722 return;
1723 }
1724 if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
1725 !port->xmit_buf) {
1726 func_exit();
1727 return;
1728 }
1729 spin_lock_irqsave(&bp->lock, flags);
1730 port->IER |= IER_TXRDY;
1731 sx_out(port_Board(port), CD186x_CAR, port_No(port));
1732 sx_out(port_Board(port), CD186x_IER, port->IER);
1733 spin_unlock_irqrestore(&bp->lock, flags);
1734
1735 func_exit();
1736}
1737
1738
1739static int sx_write_room(struct tty_struct * tty)
1740{
1741 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1742 int ret;
1743
1744 func_enter();
1745
1746 if (sx_paranoia_check(port, tty->name, "sx_write_room")) {
1747 func_exit();
1748 return 0;
1749 }
1750
1751 ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;
1752 if (ret < 0)
1753 ret = 0;
1754
1755 func_exit();
1756 return ret;
1757}
1758
1759
1760static int sx_chars_in_buffer(struct tty_struct *tty)
1761{
1762 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1763
1764 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08001765
Linus Torvalds1da177e2005-04-16 15:20:36 -07001766 if (sx_paranoia_check(port, tty->name, "sx_chars_in_buffer")) {
1767 func_exit();
1768 return 0;
1769 }
1770 func_exit();
1771 return port->xmit_cnt;
1772}
1773
1774
1775static void sx_flush_buffer(struct tty_struct *tty)
1776{
1777 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1778 unsigned long flags;
1779 struct specialix_board * bp;
1780
1781 func_enter();
1782
1783 if (sx_paranoia_check(port, tty->name, "sx_flush_buffer")) {
1784 func_exit();
1785 return;
1786 }
1787
1788 bp = port_Board(port);
1789 spin_lock_irqsave(&port->lock, flags);
1790 port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
1791 spin_unlock_irqrestore(&port->lock, flags);
1792 tty_wakeup(tty);
1793
1794 func_exit();
1795}
1796
1797
1798static int sx_tiocmget(struct tty_struct *tty, struct file *file)
1799{
1800 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1801 struct specialix_board * bp;
1802 unsigned char status;
1803 unsigned int result;
1804 unsigned long flags;
1805
1806 func_enter();
1807
1808 if (sx_paranoia_check(port, tty->name, __FUNCTION__)) {
1809 func_exit();
1810 return -ENODEV;
1811 }
1812
1813 bp = port_Board(port);
1814 spin_lock_irqsave (&bp->lock, flags);
1815 sx_out(bp, CD186x_CAR, port_No(port));
1816 status = sx_in(bp, CD186x_MSVR);
1817 spin_unlock_irqrestore(&bp->lock, flags);
1818 dprintk (SX_DEBUG_INIT, "Got msvr[%d] = %02x, car = %d.\n",
1819 port_No(port), status, sx_in (bp, CD186x_CAR));
1820 dprintk (SX_DEBUG_INIT, "sx_port = %p, port = %p\n", sx_port, port);
1821 if (SX_CRTSCTS(port->tty)) {
Jeff Garzikd61780c2005-10-30 15:01:51 -08001822 result = /* (status & MSVR_RTS) ? */ TIOCM_DTR /* : 0) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001823 | ((status & MSVR_DTR) ? TIOCM_RTS : 0)
1824 | ((status & MSVR_CD) ? TIOCM_CAR : 0)
1825 |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */
1826 | ((status & MSVR_CTS) ? TIOCM_CTS : 0);
1827 } else {
Jeff Garzikd61780c2005-10-30 15:01:51 -08001828 result = /* (status & MSVR_RTS) ? */ TIOCM_RTS /* : 0) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001829 | ((status & MSVR_DTR) ? TIOCM_DTR : 0)
1830 | ((status & MSVR_CD) ? TIOCM_CAR : 0)
1831 |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */
1832 | ((status & MSVR_CTS) ? TIOCM_CTS : 0);
1833 }
1834
1835 func_exit();
1836
1837 return result;
1838}
1839
1840
1841static int sx_tiocmset(struct tty_struct *tty, struct file *file,
1842 unsigned int set, unsigned int clear)
1843{
1844 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1845 unsigned long flags;
1846 struct specialix_board *bp;
1847
1848 func_enter();
1849
1850 if (sx_paranoia_check(port, tty->name, __FUNCTION__)) {
1851 func_exit();
1852 return -ENODEV;
1853 }
1854
1855 bp = port_Board(port);
1856
1857 spin_lock_irqsave(&port->lock, flags);
1858 /* if (set & TIOCM_RTS)
1859 port->MSVR |= MSVR_RTS; */
1860 /* if (set & TIOCM_DTR)
1861 port->MSVR |= MSVR_DTR; */
1862
1863 if (SX_CRTSCTS(port->tty)) {
1864 if (set & TIOCM_RTS)
1865 port->MSVR |= MSVR_DTR;
1866 } else {
1867 if (set & TIOCM_DTR)
1868 port->MSVR |= MSVR_DTR;
1869 }
1870
1871 /* if (clear & TIOCM_RTS)
1872 port->MSVR &= ~MSVR_RTS; */
1873 /* if (clear & TIOCM_DTR)
1874 port->MSVR &= ~MSVR_DTR; */
1875 if (SX_CRTSCTS(port->tty)) {
1876 if (clear & TIOCM_RTS)
1877 port->MSVR &= ~MSVR_DTR;
1878 } else {
1879 if (clear & TIOCM_DTR)
1880 port->MSVR &= ~MSVR_DTR;
1881 }
1882 spin_lock_irqsave(&bp->lock, flags);
1883 sx_out(bp, CD186x_CAR, port_No(port));
1884 sx_out(bp, CD186x_MSVR, port->MSVR);
1885 spin_unlock_irqrestore(&bp->lock, flags);
1886 spin_unlock_irqrestore(&port->lock, flags);
1887 func_exit();
1888 return 0;
1889}
1890
1891
1892static inline void sx_send_break(struct specialix_port * port, unsigned long length)
1893{
1894 struct specialix_board *bp = port_Board(port);
1895 unsigned long flags;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001896
Linus Torvalds1da177e2005-04-16 15:20:36 -07001897 func_enter();
1898
1899 spin_lock_irqsave (&port->lock, flags);
1900 port->break_length = SPECIALIX_TPS / HZ * length;
1901 port->COR2 |= COR2_ETC;
1902 port->IER |= IER_TXRDY;
1903 spin_lock_irqsave(&bp->lock, flags);
1904 sx_out(bp, CD186x_CAR, port_No(port));
1905 sx_out(bp, CD186x_COR2, port->COR2);
1906 sx_out(bp, CD186x_IER, port->IER);
1907 spin_unlock_irqrestore(&bp->lock, flags);
1908 spin_unlock_irqrestore (&port->lock, flags);
1909 sx_wait_CCR(bp);
1910 spin_lock_irqsave(&bp->lock, flags);
1911 sx_out(bp, CD186x_CCR, CCR_CORCHG2);
1912 spin_unlock_irqrestore(&bp->lock, flags);
1913 sx_wait_CCR(bp);
1914
1915 func_exit();
1916}
1917
1918
1919static inline int sx_set_serial_info(struct specialix_port * port,
1920 struct serial_struct __user * newinfo)
1921{
1922 struct serial_struct tmp;
1923 struct specialix_board *bp = port_Board(port);
1924 int change_speed;
1925
1926 func_enter();
1927 /*
Jesper Juhle49332b2005-05-01 08:59:08 -07001928 if (!access_ok(VERIFY_READ, (void *) newinfo, sizeof(tmp))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001929 func_exit();
Jesper Juhle49332b2005-05-01 08:59:08 -07001930 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001931 }
1932 */
1933 if (copy_from_user(&tmp, newinfo, sizeof(tmp))) {
1934 func_enter();
1935 return -EFAULT;
1936 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001937
1938#if 0
Linus Torvalds1da177e2005-04-16 15:20:36 -07001939 if ((tmp.irq != bp->irq) ||
1940 (tmp.port != bp->base) ||
1941 (tmp.type != PORT_CIRRUS) ||
1942 (tmp.baud_base != (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC) ||
1943 (tmp.custom_divisor != 0) ||
1944 (tmp.xmit_fifo_size != CD186x_NFIFO) ||
1945 (tmp.flags & ~SPECIALIX_LEGAL_FLAGS)) {
1946 func_exit();
1947 return -EINVAL;
1948 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001949#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001950
1951 change_speed = ((port->flags & ASYNC_SPD_MASK) !=
1952 (tmp.flags & ASYNC_SPD_MASK));
1953 change_speed |= (tmp.custom_divisor != port->custom_divisor);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001954
Linus Torvalds1da177e2005-04-16 15:20:36 -07001955 if (!capable(CAP_SYS_ADMIN)) {
1956 if ((tmp.close_delay != port->close_delay) ||
1957 (tmp.closing_wait != port->closing_wait) ||
1958 ((tmp.flags & ~ASYNC_USR_MASK) !=
1959 (port->flags & ~ASYNC_USR_MASK))) {
1960 func_exit();
1961 return -EPERM;
1962 }
1963 port->flags = ((port->flags & ~ASYNC_USR_MASK) |
1964 (tmp.flags & ASYNC_USR_MASK));
1965 port->custom_divisor = tmp.custom_divisor;
1966 } else {
1967 port->flags = ((port->flags & ~ASYNC_FLAGS) |
1968 (tmp.flags & ASYNC_FLAGS));
1969 port->close_delay = tmp.close_delay;
1970 port->closing_wait = tmp.closing_wait;
1971 port->custom_divisor = tmp.custom_divisor;
1972 }
1973 if (change_speed) {
1974 sx_change_speed(bp, port);
1975 }
1976 func_exit();
1977 return 0;
1978}
1979
1980
1981static inline int sx_get_serial_info(struct specialix_port * port,
1982 struct serial_struct __user *retinfo)
1983{
1984 struct serial_struct tmp;
1985 struct specialix_board *bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001986
Linus Torvalds1da177e2005-04-16 15:20:36 -07001987 func_enter();
1988
1989 /*
Jesper Juhle49332b2005-05-01 08:59:08 -07001990 if (!access_ok(VERIFY_WRITE, (void *) retinfo, sizeof(tmp)))
1991 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001992 */
1993
1994 memset(&tmp, 0, sizeof(tmp));
1995 tmp.type = PORT_CIRRUS;
1996 tmp.line = port - sx_port;
1997 tmp.port = bp->base;
1998 tmp.irq = bp->irq;
1999 tmp.flags = port->flags;
2000 tmp.baud_base = (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC;
2001 tmp.close_delay = port->close_delay * HZ/100;
2002 tmp.closing_wait = port->closing_wait * HZ/100;
2003 tmp.custom_divisor = port->custom_divisor;
2004 tmp.xmit_fifo_size = CD186x_NFIFO;
2005 if (copy_to_user(retinfo, &tmp, sizeof(tmp))) {
2006 func_exit();
2007 return -EFAULT;
2008 }
2009
2010 func_exit();
2011 return 0;
2012}
2013
2014
Jeff Garzikd61780c2005-10-30 15:01:51 -08002015static int sx_ioctl(struct tty_struct * tty, struct file * filp,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002016 unsigned int cmd, unsigned long arg)
2017{
2018 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2019 int retval;
2020 void __user *argp = (void __user *)arg;
2021
2022 func_enter();
2023
2024 if (sx_paranoia_check(port, tty->name, "sx_ioctl")) {
2025 func_exit();
2026 return -ENODEV;
2027 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002028
Linus Torvalds1da177e2005-04-16 15:20:36 -07002029 switch (cmd) {
2030 case TCSBRK: /* SVID version: non-zero arg --> no break */
2031 retval = tty_check_change(tty);
2032 if (retval) {
2033 func_exit();
2034 return retval;
2035 }
2036 tty_wait_until_sent(tty, 0);
2037 if (!arg)
2038 sx_send_break(port, HZ/4); /* 1/4 second */
2039 return 0;
2040 case TCSBRKP: /* support for POSIX tcsendbreak() */
2041 retval = tty_check_change(tty);
2042 if (retval) {
2043 func_exit();
2044 return retval;
2045 }
2046 tty_wait_until_sent(tty, 0);
2047 sx_send_break(port, arg ? arg*(HZ/10) : HZ/4);
2048 func_exit();
2049 return 0;
2050 case TIOCGSOFTCAR:
2051 if (put_user(C_CLOCAL(tty)?1:0, (unsigned long __user *)argp)) {
2052 func_exit();
2053 return -EFAULT;
2054 }
2055 func_exit();
2056 return 0;
2057 case TIOCSSOFTCAR:
2058 if (get_user(arg, (unsigned long __user *) argp)) {
2059 func_exit();
2060 return -EFAULT;
2061 }
2062 tty->termios->c_cflag =
2063 ((tty->termios->c_cflag & ~CLOCAL) |
2064 (arg ? CLOCAL : 0));
2065 func_exit();
2066 return 0;
2067 case TIOCGSERIAL:
2068 func_exit();
2069 return sx_get_serial_info(port, argp);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002070 case TIOCSSERIAL:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002071 func_exit();
2072 return sx_set_serial_info(port, argp);
2073 default:
2074 func_exit();
2075 return -ENOIOCTLCMD;
2076 }
2077 func_exit();
2078 return 0;
2079}
2080
2081
2082static void sx_throttle(struct tty_struct * tty)
2083{
2084 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2085 struct specialix_board *bp;
2086 unsigned long flags;
2087
2088 func_enter();
2089
2090 if (sx_paranoia_check(port, tty->name, "sx_throttle")) {
2091 func_exit();
2092 return;
2093 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002094
Linus Torvalds1da177e2005-04-16 15:20:36 -07002095 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002096
Linus Torvalds1da177e2005-04-16 15:20:36 -07002097 /* Use DTR instead of RTS ! */
Jeff Garzikd61780c2005-10-30 15:01:51 -08002098 if (SX_CRTSCTS (tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002099 port->MSVR &= ~MSVR_DTR;
2100 else {
2101 /* Auch!!! I think the system shouldn't call this then. */
2102 /* Or maybe we're supposed (allowed?) to do our side of hw
Jeff Garzikd61780c2005-10-30 15:01:51 -08002103 handshake anyway, even when hardware handshake is off.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002104 When you see this in your logs, please report.... */
2105 printk (KERN_ERR "sx%d: Need to throttle, but can't (hardware hs is off)\n",
2106 port_No (port));
2107 }
2108 spin_lock_irqsave(&bp->lock, flags);
2109 sx_out(bp, CD186x_CAR, port_No(port));
2110 spin_unlock_irqrestore(&bp->lock, flags);
2111 if (I_IXOFF(tty)) {
2112 spin_unlock_irqrestore(&bp->lock, flags);
2113 sx_wait_CCR(bp);
2114 spin_lock_irqsave(&bp->lock, flags);
2115 sx_out(bp, CD186x_CCR, CCR_SSCH2);
2116 spin_unlock_irqrestore(&bp->lock, flags);
2117 sx_wait_CCR(bp);
2118 }
2119 spin_lock_irqsave(&bp->lock, flags);
2120 sx_out(bp, CD186x_MSVR, port->MSVR);
2121 spin_unlock_irqrestore(&bp->lock, flags);
2122
2123 func_exit();
2124}
2125
2126
2127static void sx_unthrottle(struct tty_struct * tty)
2128{
2129 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2130 struct specialix_board *bp;
2131 unsigned long flags;
2132
2133 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002134
Linus Torvalds1da177e2005-04-16 15:20:36 -07002135 if (sx_paranoia_check(port, tty->name, "sx_unthrottle")) {
2136 func_exit();
2137 return;
2138 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002139
Linus Torvalds1da177e2005-04-16 15:20:36 -07002140 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002141
Linus Torvalds1da177e2005-04-16 15:20:36 -07002142 spin_lock_irqsave(&port->lock, flags);
2143 /* XXXX Use DTR INSTEAD???? */
2144 if (SX_CRTSCTS(tty)) {
2145 port->MSVR |= MSVR_DTR;
2146 } /* Else clause: see remark in "sx_throttle"... */
2147 spin_lock_irqsave(&bp->lock, flags);
2148 sx_out(bp, CD186x_CAR, port_No(port));
2149 spin_unlock_irqrestore(&bp->lock, flags);
2150 if (I_IXOFF(tty)) {
2151 spin_unlock_irqrestore(&port->lock, flags);
2152 sx_wait_CCR(bp);
2153 spin_lock_irqsave(&bp->lock, flags);
2154 sx_out(bp, CD186x_CCR, CCR_SSCH1);
2155 spin_unlock_irqrestore(&bp->lock, flags);
2156 sx_wait_CCR(bp);
2157 spin_lock_irqsave(&port->lock, flags);
2158 }
2159 spin_lock_irqsave(&bp->lock, flags);
2160 sx_out(bp, CD186x_MSVR, port->MSVR);
2161 spin_unlock_irqrestore(&bp->lock, flags);
2162 spin_unlock_irqrestore(&port->lock, flags);
2163
2164 func_exit();
2165}
2166
2167
2168static void sx_stop(struct tty_struct * tty)
2169{
2170 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2171 struct specialix_board *bp;
2172 unsigned long flags;
2173
2174 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002175
Linus Torvalds1da177e2005-04-16 15:20:36 -07002176 if (sx_paranoia_check(port, tty->name, "sx_stop")) {
2177 func_exit();
2178 return;
2179 }
2180
2181 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002182
Linus Torvalds1da177e2005-04-16 15:20:36 -07002183 spin_lock_irqsave(&port->lock, flags);
2184 port->IER &= ~IER_TXRDY;
2185 spin_lock_irqsave(&bp->lock, flags);
2186 sx_out(bp, CD186x_CAR, port_No(port));
2187 sx_out(bp, CD186x_IER, port->IER);
2188 spin_unlock_irqrestore(&bp->lock, flags);
2189 spin_unlock_irqrestore(&port->lock, flags);
2190
2191 func_exit();
2192}
2193
2194
2195static void sx_start(struct tty_struct * tty)
2196{
2197 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2198 struct specialix_board *bp;
2199 unsigned long flags;
2200
2201 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002202
Linus Torvalds1da177e2005-04-16 15:20:36 -07002203 if (sx_paranoia_check(port, tty->name, "sx_start")) {
2204 func_exit();
2205 return;
2206 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002207
Linus Torvalds1da177e2005-04-16 15:20:36 -07002208 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002209
Linus Torvalds1da177e2005-04-16 15:20:36 -07002210 spin_lock_irqsave(&port->lock, flags);
2211 if (port->xmit_cnt && port->xmit_buf && !(port->IER & IER_TXRDY)) {
2212 port->IER |= IER_TXRDY;
2213 spin_lock_irqsave(&bp->lock, flags);
2214 sx_out(bp, CD186x_CAR, port_No(port));
2215 sx_out(bp, CD186x_IER, port->IER);
2216 spin_unlock_irqrestore(&bp->lock, flags);
2217 }
2218 spin_unlock_irqrestore(&port->lock, flags);
2219
2220 func_exit();
2221}
2222
Linus Torvalds1da177e2005-04-16 15:20:36 -07002223static void sx_hangup(struct tty_struct * tty)
2224{
2225 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2226 struct specialix_board *bp;
2227 unsigned long flags;
2228
2229 func_enter();
2230
2231 if (sx_paranoia_check(port, tty->name, "sx_hangup")) {
2232 func_exit();
2233 return;
2234 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002235
Linus Torvalds1da177e2005-04-16 15:20:36 -07002236 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002237
Linus Torvalds1da177e2005-04-16 15:20:36 -07002238 sx_shutdown_port(bp, port);
2239 spin_lock_irqsave(&port->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002240 bp->count -= port->count;
2241 if (bp->count < 0) {
2242 printk(KERN_ERR "sx%d: sx_hangup: bad board count: %d port: %d\n",
2243 board_No(bp), bp->count, tty->index);
2244 bp->count = 0;
2245 }
2246 port->count = 0;
2247 port->flags &= ~ASYNC_NORMAL_ACTIVE;
2248 port->tty = NULL;
2249 spin_unlock_irqrestore(&port->lock, flags);
2250 wake_up_interruptible(&port->open_wait);
2251
2252 func_exit();
2253}
2254
2255
Alan Cox606d0992006-12-08 02:38:45 -08002256static void sx_set_termios(struct tty_struct * tty, struct ktermios * old_termios)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002257{
2258 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2259 unsigned long flags;
2260 struct specialix_board * bp;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002261
Linus Torvalds1da177e2005-04-16 15:20:36 -07002262 if (sx_paranoia_check(port, tty->name, "sx_set_termios"))
2263 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002264
Linus Torvalds1da177e2005-04-16 15:20:36 -07002265 if (tty->termios->c_cflag == old_termios->c_cflag &&
2266 tty->termios->c_iflag == old_termios->c_iflag)
2267 return;
2268
2269 bp = port_Board(port);
2270 spin_lock_irqsave(&port->lock, flags);
2271 sx_change_speed(port_Board(port), port);
2272 spin_unlock_irqrestore(&port->lock, flags);
2273
2274 if ((old_termios->c_cflag & CRTSCTS) &&
2275 !(tty->termios->c_cflag & CRTSCTS)) {
2276 tty->hw_stopped = 0;
2277 sx_start(tty);
2278 }
2279}
2280
Jeff Dikeb68e31d2006-10-02 02:17:18 -07002281static const struct tty_operations sx_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002282 .open = sx_open,
2283 .close = sx_close,
2284 .write = sx_write,
2285 .put_char = sx_put_char,
2286 .flush_chars = sx_flush_chars,
2287 .write_room = sx_write_room,
2288 .chars_in_buffer = sx_chars_in_buffer,
2289 .flush_buffer = sx_flush_buffer,
2290 .ioctl = sx_ioctl,
2291 .throttle = sx_throttle,
2292 .unthrottle = sx_unthrottle,
2293 .set_termios = sx_set_termios,
2294 .stop = sx_stop,
2295 .start = sx_start,
2296 .hangup = sx_hangup,
2297 .tiocmget = sx_tiocmget,
2298 .tiocmset = sx_tiocmset,
2299};
2300
2301static int sx_init_drivers(void)
2302{
2303 int error;
2304 int i;
2305
2306 func_enter();
2307
2308 specialix_driver = alloc_tty_driver(SX_NBOARD * SX_NPORT);
2309 if (!specialix_driver) {
2310 printk(KERN_ERR "sx: Couldn't allocate tty_driver.\n");
2311 func_exit();
2312 return 1;
2313 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002314
Linus Torvalds1da177e2005-04-16 15:20:36 -07002315 specialix_driver->owner = THIS_MODULE;
2316 specialix_driver->name = "ttyW";
2317 specialix_driver->major = SPECIALIX_NORMAL_MAJOR;
2318 specialix_driver->type = TTY_DRIVER_TYPE_SERIAL;
2319 specialix_driver->subtype = SERIAL_TYPE_NORMAL;
2320 specialix_driver->init_termios = tty_std_termios;
2321 specialix_driver->init_termios.c_cflag =
2322 B9600 | CS8 | CREAD | HUPCL | CLOCAL;
Alan Cox606d0992006-12-08 02:38:45 -08002323 specialix_driver->init_termios.c_ispeed = 9600;
2324 specialix_driver->init_termios.c_ospeed = 9600;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002325 specialix_driver->flags = TTY_DRIVER_REAL_RAW;
2326 tty_set_operations(specialix_driver, &sx_ops);
2327
2328 if ((error = tty_register_driver(specialix_driver))) {
2329 put_tty_driver(specialix_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002330 printk(KERN_ERR "sx: Couldn't register specialix IO8+ driver, error = %d\n",
2331 error);
2332 func_exit();
2333 return 1;
2334 }
2335 memset(sx_port, 0, sizeof(sx_port));
2336 for (i = 0; i < SX_NPORT * SX_NBOARD; i++) {
2337 sx_port[i].magic = SPECIALIX_MAGIC;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002338 sx_port[i].close_delay = 50 * HZ/100;
2339 sx_port[i].closing_wait = 3000 * HZ/100;
2340 init_waitqueue_head(&sx_port[i].open_wait);
2341 init_waitqueue_head(&sx_port[i].close_wait);
2342 spin_lock_init(&sx_port[i].lock);
2343 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002344
Linus Torvalds1da177e2005-04-16 15:20:36 -07002345 func_exit();
2346 return 0;
2347}
2348
2349static void sx_release_drivers(void)
2350{
2351 func_enter();
2352
Linus Torvalds1da177e2005-04-16 15:20:36 -07002353 tty_unregister_driver(specialix_driver);
2354 put_tty_driver(specialix_driver);
2355 func_exit();
2356}
2357
Jeff Garzikd61780c2005-10-30 15:01:51 -08002358/*
2359 * This routine must be called by kernel at boot time
Linus Torvalds1da177e2005-04-16 15:20:36 -07002360 */
2361static int __init specialix_init(void)
2362{
2363 int i;
2364 int found = 0;
2365
2366 func_enter();
2367
2368 printk(KERN_INFO "sx: Specialix IO8+ driver v" VERSION ", (c) R.E.Wolff 1997/1998.\n");
2369 printk(KERN_INFO "sx: derived from work (c) D.Gorodchanin 1994-1996.\n");
2370#ifdef CONFIG_SPECIALIX_RTSCTS
2371 printk (KERN_INFO "sx: DTR/RTS pin is always RTS.\n");
2372#else
2373 printk (KERN_INFO "sx: DTR/RTS pin is RTS when CRTSCTS is on.\n");
2374#endif
Jeff Garzikd61780c2005-10-30 15:01:51 -08002375
Linus Torvalds1da177e2005-04-16 15:20:36 -07002376 for (i = 0; i < SX_NBOARD; i++)
Ingo Molnar34af9462006-06-27 02:53:55 -07002377 spin_lock_init(&sx_board[i].lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002378
2379 if (sx_init_drivers()) {
2380 func_exit();
2381 return -EIO;
2382 }
2383
Jeff Garzikd61780c2005-10-30 15:01:51 -08002384 for (i = 0; i < SX_NBOARD; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002385 if (sx_board[i].base && !sx_probe(&sx_board[i]))
2386 found++;
2387
2388#ifdef CONFIG_PCI
2389 {
2390 struct pci_dev *pdev = NULL;
2391
2392 i=0;
2393 while (i < SX_NBOARD) {
2394 if (sx_board[i].flags & SX_BOARD_PRESENT) {
2395 i++;
2396 continue;
2397 }
Alan Cox606d0992006-12-08 02:38:45 -08002398 pdev = pci_get_device (PCI_VENDOR_ID_SPECIALIX,
Jeff Garzikd61780c2005-10-30 15:01:51 -08002399 PCI_DEVICE_ID_SPECIALIX_IO8,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002400 pdev);
2401 if (!pdev) break;
2402
2403 if (pci_enable_device(pdev))
2404 continue;
2405
2406 sx_board[i].irq = pdev->irq;
2407
2408 sx_board[i].base = pci_resource_start (pdev, 2);
2409
2410 sx_board[i].flags |= SX_BOARD_IS_PCI;
2411 if (!sx_probe(&sx_board[i]))
2412 found ++;
2413 }
Alan Cox606d0992006-12-08 02:38:45 -08002414 /* May exit pci_get sequence early with lots of boards */
2415 if (pdev != NULL)
2416 pci_dev_put(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002417 }
2418#endif
2419
2420 if (!found) {
2421 sx_release_drivers();
2422 printk(KERN_INFO "sx: No specialix IO8+ boards detected.\n");
2423 func_exit();
2424 return -EIO;
2425 }
2426
2427 func_exit();
2428 return 0;
2429}
2430
2431static int iobase[SX_NBOARD] = {0,};
2432
2433static int irq [SX_NBOARD] = {0,};
2434
2435module_param_array(iobase, int, NULL, 0);
2436module_param_array(irq, int, NULL, 0);
2437module_param(sx_debug, int, 0);
2438module_param(sx_rxfifo, int, 0);
2439#ifdef SPECIALIX_TIMER
2440module_param(sx_poll, int, 0);
2441#endif
2442
2443/*
2444 * You can setup up to 4 boards.
2445 * by specifying "iobase=0xXXX,0xXXX ..." as insmod parameter.
Jeff Garzikd61780c2005-10-30 15:01:51 -08002446 * You should specify the IRQs too in that case "irq=....,...".
2447 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07002448 * More than 4 boards in one computer is not possible, as the card can
Jeff Garzikd61780c2005-10-30 15:01:51 -08002449 * only use 4 different interrupts.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002450 *
2451 */
2452static int __init specialix_init_module(void)
2453{
2454 int i;
2455
2456 func_enter();
2457
Linus Torvalds1da177e2005-04-16 15:20:36 -07002458 if (iobase[0] || iobase[1] || iobase[2] || iobase[3]) {
2459 for(i = 0; i < SX_NBOARD; i++) {
2460 sx_board[i].base = iobase[i];
2461 sx_board[i].irq = irq[i];
2462 sx_board[i].count= 0;
2463 }
2464 }
2465
2466 func_exit();
2467
2468 return specialix_init();
2469}
Jeff Garzikd61780c2005-10-30 15:01:51 -08002470
Linus Torvalds1da177e2005-04-16 15:20:36 -07002471static void __exit specialix_exit_module(void)
2472{
2473 int i;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002474
Linus Torvalds1da177e2005-04-16 15:20:36 -07002475 func_enter();
2476
2477 sx_release_drivers();
2478 for (i = 0; i < SX_NBOARD; i++)
Jeff Garzikd61780c2005-10-30 15:01:51 -08002479 if (sx_board[i].flags & SX_BOARD_PRESENT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002480 sx_release_io_range(&sx_board[i]);
2481#ifdef SPECIALIX_TIMER
Jiri Slaby40565f12007-02-12 00:52:31 -08002482 del_timer_sync(&missed_irq_timer);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002483#endif
2484
2485 func_exit();
2486}
2487
Chuck Short7691030b2006-07-10 04:43:59 -07002488static struct pci_device_id specialx_pci_tbl[] __devinitdata = {
2489 { PCI_DEVICE(PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_SPECIALIX_IO8) },
2490 { }
2491};
2492MODULE_DEVICE_TABLE(pci, specialx_pci_tbl);
2493
Linus Torvalds1da177e2005-04-16 15:20:36 -07002494module_init(specialix_init_module);
2495module_exit(specialix_exit_module);
2496
2497MODULE_LICENSE("GPL");