blob: baf7234b6e662fd8277368ecdf854ca2246de4d6 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * specialix.c -- specialix IO8+ multiport serial driver.
3 *
4 * Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl)
5 * Copyright (C) 1994-1996 Dmitry Gorodchanin (pgmdsg@ibi.com)
6 *
7 * Specialix pays for the development and support of this driver.
8 * Please DO contact io8-linux@specialix.co.uk if you require
9 * support. But please read the documentation (specialix.txt)
10 * first.
11 *
12 * This driver was developped in the BitWizard linux device
13 * driver service. If you require a linux device driver for your
14 * product, please contact devices@BitWizard.nl for a quote.
15 *
16 * This code is firmly based on the riscom/8 serial driver,
17 * written by Dmitry Gorodchanin. The specialix IO8+ card
18 * programming information was obtained from the CL-CD1865 Data
19 * Book, and Specialix document number 6200059: IO8+ Hardware
20 * Functional Specification.
21 *
22 * This program is free software; you can redistribute it and/or
23 * modify it under the terms of the GNU General Public License as
24 * published by the Free Software Foundation; either version 2 of
25 * the License, or (at your option) any later version.
26 *
27 * This program is distributed in the hope that it will be
28 * useful, but WITHOUT ANY WARRANTY; without even the implied
29 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
30 * PURPOSE. See the GNU General Public License for more details.
31 *
32 * You should have received a copy of the GNU General Public
33 * License along with this program; if not, write to the Free
34 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
35 * USA.
36 *
37 * Revision history:
38 *
39 * Revision 1.0: April 1st 1997.
40 * Initial release for alpha testing.
Jeff Garzikd61780c2005-10-30 15:01:51 -080041 * Revision 1.1: April 14th 1997.
42 * Incorporated Richard Hudsons suggestions,
Linus Torvalds1da177e2005-04-16 15:20:36 -070043 * removed some debugging printk's.
44 * Revision 1.2: April 15th 1997.
45 * Ported to 2.1.x kernels.
Jeff Garzikd61780c2005-10-30 15:01:51 -080046 * Revision 1.3: April 17th 1997
47 * Backported to 2.0. (Compatibility macros).
Linus Torvalds1da177e2005-04-16 15:20:36 -070048 * Revision 1.4: April 18th 1997
Jeff Garzikd61780c2005-10-30 15:01:51 -080049 * Fixed DTR/RTS bug that caused the card to indicate
50 * "don't send data" to a modem after the password prompt.
Linus Torvalds1da177e2005-04-16 15:20:36 -070051 * Fixed bug for premature (fake) interrupts.
52 * Revision 1.5: April 19th 1997
Jeff Garzikd61780c2005-10-30 15:01:51 -080053 * fixed a minor typo in the header file, cleanup a little.
Linus Torvalds1da177e2005-04-16 15:20:36 -070054 * performance warnings are now MAXed at once per minute.
55 * Revision 1.6: May 23 1997
56 * Changed the specialix=... format to include interrupt.
57 * Revision 1.7: May 27 1997
58 * Made many more debug printk's a compile time option.
59 * Revision 1.8: Jul 1 1997
60 * port to linux-2.1.43 kernel.
61 * Revision 1.9: Oct 9 1998
62 * Added stuff for the IO8+/PCI version.
Jeff Garzikd61780c2005-10-30 15:01:51 -080063 * Revision 1.10: Oct 22 1999 / Jan 21 2000.
64 * Added stuff for setserial.
Linus Torvalds1da177e2005-04-16 15:20:36 -070065 * Nicolas Mailhot (Nicolas.Mailhot@email.enst.fr)
Jeff Garzikd61780c2005-10-30 15:01:51 -080066 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070067 */
68
69#define VERSION "1.11"
70
71
72/*
73 * There is a bunch of documentation about the card, jumpers, config
74 * settings, restrictions, cables, device names and numbers in
75 * Documentation/specialix.txt
76 */
77
Linus Torvalds1da177e2005-04-16 15:20:36 -070078#include <linux/module.h>
79
80#include <asm/io.h>
81#include <linux/kernel.h>
82#include <linux/sched.h>
83#include <linux/ioport.h>
84#include <linux/interrupt.h>
85#include <linux/errno.h>
86#include <linux/tty.h>
Alan Cox33f0f882006-01-09 20:54:13 -080087#include <linux/tty_flip.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070088#include <linux/mm.h>
89#include <linux/serial.h>
90#include <linux/fcntl.h>
91#include <linux/major.h>
92#include <linux/delay.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070093#include <linux/pci.h>
94#include <linux/init.h>
95#include <asm/uaccess.h>
96
97#include "specialix_io8.h"
98#include "cd1865.h"
99
100
101/*
102 This driver can spew a whole lot of debugging output at you. If you
103 need maximum performance, you should disable the DEBUG define. To
104 aid in debugging in the field, I'm leaving the compile-time debug
105 features enabled, and disable them "runtime". That allows me to
106 instruct people with problems to enable debugging without requiring
107 them to recompile...
108*/
109#define DEBUG
110
111static int sx_debug;
112static int sx_rxfifo = SPECIALIX_RXFIFO;
113
114#ifdef DEBUG
115#define dprintk(f, str...) if (sx_debug & f) printk (str)
116#else
117#define dprintk(f, str...) /* nothing */
118#endif
119
120#define SX_DEBUG_FLOW 0x0001
121#define SX_DEBUG_DATA 0x0002
122#define SX_DEBUG_PROBE 0x0004
123#define SX_DEBUG_CHAN 0x0008
124#define SX_DEBUG_INIT 0x0010
125#define SX_DEBUG_RX 0x0020
126#define SX_DEBUG_TX 0x0040
127#define SX_DEBUG_IRQ 0x0080
128#define SX_DEBUG_OPEN 0x0100
129#define SX_DEBUG_TERMIOS 0x0200
130#define SX_DEBUG_SIGNALS 0x0400
131#define SX_DEBUG_FIFO 0x0800
132
133
134#define func_enter() dprintk (SX_DEBUG_FLOW, "io8: enter %s\n",__FUNCTION__)
135#define func_exit() dprintk (SX_DEBUG_FLOW, "io8: exit %s\n", __FUNCTION__)
136
137#define jiffies_from_ms(a) ((((a) * HZ)/1000)+1)
138
139
140/* Configurable options: */
141
142/* Am I paranoid or not ? ;-) */
143#define SPECIALIX_PARANOIA_CHECK
144
145/* Do I trust the IRQ from the card? (enabeling it doesn't seem to help)
146 When the IRQ routine leaves the chip in a state that is keeps on
147 requiring attention, the timer doesn't help either. */
148#undef SPECIALIX_TIMER
149
150#ifdef SPECIALIX_TIMER
151static int sx_poll = HZ;
152#endif
153
154
155
Jeff Garzikd61780c2005-10-30 15:01:51 -0800156/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157 * The following defines are mostly for testing purposes. But if you need
158 * some nice reporting in your syslog, you can define them also.
159 */
160#undef SX_REPORT_FIFO
161#undef SX_REPORT_OVERRUN
162
163
164
165#ifdef CONFIG_SPECIALIX_RTSCTS
166#define SX_CRTSCTS(bla) 1
167#else
168#define SX_CRTSCTS(tty) C_CRTSCTS(tty)
169#endif
170
171
172/* Used to be outb (0xff, 0x80); */
173#define short_pause() udelay (1)
174
175
176#define SPECIALIX_LEGAL_FLAGS \
177 (ASYNC_HUP_NOTIFY | ASYNC_SAK | ASYNC_SPLIT_TERMIOS | \
178 ASYNC_SPD_HI | ASYNC_SPEED_VHI | ASYNC_SESSION_LOCKOUT | \
179 ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP)
180
181#undef RS_EVENT_WRITE_WAKEUP
182#define RS_EVENT_WRITE_WAKEUP 0
183
184static struct tty_driver *specialix_driver;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186static struct specialix_board sx_board[SX_NBOARD] = {
187 { 0, SX_IOBASE1, 9, },
188 { 0, SX_IOBASE2, 11, },
189 { 0, SX_IOBASE3, 12, },
190 { 0, SX_IOBASE4, 15, },
191};
192
193static struct specialix_port sx_port[SX_NBOARD * SX_NPORT];
194
195
196#ifdef SPECIALIX_TIMER
197static struct timer_list missed_irq_timer;
David Howells7d12e782006-10-05 14:55:46 +0100198static irqreturn_t sx_interrupt(int irq, void * dev_id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199#endif
200
201
202
203static inline int sx_paranoia_check(struct specialix_port const * port,
204 char *name, const char *routine)
205{
206#ifdef SPECIALIX_PARANOIA_CHECK
207 static const char *badmagic =
208 KERN_ERR "sx: Warning: bad specialix port magic number for device %s in %s\n";
209 static const char *badinfo =
210 KERN_ERR "sx: Warning: null specialix port for device %s in %s\n";
Jeff Garzikd61780c2005-10-30 15:01:51 -0800211
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212 if (!port) {
213 printk(badinfo, name, routine);
214 return 1;
215 }
216 if (port->magic != SPECIALIX_MAGIC) {
217 printk(badmagic, name, routine);
218 return 1;
219 }
220#endif
221 return 0;
222}
223
224
225/*
Jeff Garzikd61780c2005-10-30 15:01:51 -0800226 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 * Service functions for specialix IO8+ driver.
Jeff Garzikd61780c2005-10-30 15:01:51 -0800228 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229 */
230
231/* Get board number from pointer */
232static inline int board_No (struct specialix_board * bp)
233{
234 return bp - sx_board;
235}
236
237
238/* Get port number from pointer */
239static inline int port_No (struct specialix_port const * port)
240{
Jeff Garzikd61780c2005-10-30 15:01:51 -0800241 return SX_PORT(port - sx_port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242}
243
244
245/* Get pointer to board from pointer to port */
246static inline struct specialix_board * port_Board(struct specialix_port const * port)
247{
248 return &sx_board[SX_BOARD(port - sx_port)];
249}
250
251
252/* Input Byte from CL CD186x register */
253static inline unsigned char sx_in(struct specialix_board * bp, unsigned short reg)
254{
255 bp->reg = reg | 0x80;
256 outb (reg | 0x80, bp->base + SX_ADDR_REG);
257 return inb (bp->base + SX_DATA_REG);
258}
259
260
261/* Output Byte to CL CD186x register */
262static inline void sx_out(struct specialix_board * bp, unsigned short reg,
263 unsigned char val)
264{
265 bp->reg = reg | 0x80;
266 outb (reg | 0x80, bp->base + SX_ADDR_REG);
267 outb (val, bp->base + SX_DATA_REG);
268}
269
270
271/* Input Byte from CL CD186x register */
272static inline unsigned char sx_in_off(struct specialix_board * bp, unsigned short reg)
273{
274 bp->reg = reg;
275 outb (reg, bp->base + SX_ADDR_REG);
276 return inb (bp->base + SX_DATA_REG);
277}
278
279
280/* Output Byte to CL CD186x register */
281static inline void sx_out_off(struct specialix_board * bp, unsigned short reg,
282 unsigned char val)
283{
284 bp->reg = reg;
285 outb (reg, bp->base + SX_ADDR_REG);
286 outb (val, bp->base + SX_DATA_REG);
287}
288
289
290/* Wait for Channel Command Register ready */
291static inline void sx_wait_CCR(struct specialix_board * bp)
292{
293 unsigned long delay, flags;
294 unsigned char ccr;
295
296 for (delay = SX_CCR_TIMEOUT; delay; delay--) {
297 spin_lock_irqsave(&bp->lock, flags);
298 ccr = sx_in(bp, CD186x_CCR);
299 spin_unlock_irqrestore(&bp->lock, flags);
300 if (!ccr)
301 return;
302 udelay (1);
303 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800304
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305 printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp));
306}
307
308
309/* Wait for Channel Command Register ready */
310static inline void sx_wait_CCR_off(struct specialix_board * bp)
311{
312 unsigned long delay;
313 unsigned char crr;
314 unsigned long flags;
315
316 for (delay = SX_CCR_TIMEOUT; delay; delay--) {
317 spin_lock_irqsave(&bp->lock, flags);
318 crr = sx_in_off(bp, CD186x_CCR);
319 spin_unlock_irqrestore(&bp->lock, flags);
320 if (!crr)
321 return;
322 udelay (1);
323 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800324
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp));
326}
327
328
329/*
330 * specialix IO8+ IO range functions.
331 */
332
Jeff Garzikd61780c2005-10-30 15:01:51 -0800333static inline int sx_request_io_range(struct specialix_board * bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334{
Jeff Garzikd61780c2005-10-30 15:01:51 -0800335 return request_region(bp->base,
336 bp->flags & SX_BOARD_IS_PCI ? SX_PCI_IO_SPACE : SX_IO_SPACE,
337 "specialix IO8+") == NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338}
339
340
341static inline void sx_release_io_range(struct specialix_board * bp)
342{
Jeff Garzikd61780c2005-10-30 15:01:51 -0800343 release_region(bp->base,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 bp->flags&SX_BOARD_IS_PCI?SX_PCI_IO_SPACE:SX_IO_SPACE);
345}
346
Jeff Garzikd61780c2005-10-30 15:01:51 -0800347
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348/* Must be called with enabled interrupts */
Jeff Garzikd61780c2005-10-30 15:01:51 -0800349/* Ugly. Very ugly. Don't use this for anything else than initialization
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350 code */
351static inline void sx_long_delay(unsigned long delay)
352{
353 unsigned long i;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800354
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355 for (i = jiffies + delay; time_after(i, jiffies); ) ;
356}
357
358
359
360/* Set the IRQ using the RTS lines that run to the PAL on the board.... */
361static int sx_set_irq ( struct specialix_board *bp)
362{
363 int virq;
364 int i;
365 unsigned long flags;
366
Jeff Garzikd61780c2005-10-30 15:01:51 -0800367 if (bp->flags & SX_BOARD_IS_PCI)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 return 1;
369 switch (bp->irq) {
370 /* In the same order as in the docs... */
371 case 15: virq = 0;break;
372 case 12: virq = 1;break;
373 case 11: virq = 2;break;
374 case 9: virq = 3;break;
375 default: printk (KERN_ERR "Speclialix: cannot set irq to %d.\n", bp->irq);
376 return 0;
377 }
378 spin_lock_irqsave(&bp->lock, flags);
379 for (i=0;i<2;i++) {
380 sx_out(bp, CD186x_CAR, i);
381 sx_out(bp, CD186x_MSVRTS, ((virq >> i) & 0x1)? MSVR_RTS:0);
382 }
383 spin_unlock_irqrestore(&bp->lock, flags);
384 return 1;
385}
386
387
388/* Reset and setup CD186x chip */
389static int sx_init_CD186x(struct specialix_board * bp)
390{
391 unsigned long flags;
392 int scaler;
393 int rv = 1;
394
395 func_enter();
396 sx_wait_CCR_off(bp); /* Wait for CCR ready */
397 spin_lock_irqsave(&bp->lock, flags);
398 sx_out_off(bp, CD186x_CCR, CCR_HARDRESET); /* Reset CD186x chip */
399 spin_unlock_irqrestore(&bp->lock, flags);
400 sx_long_delay(HZ/20); /* Delay 0.05 sec */
401 spin_lock_irqsave(&bp->lock, flags);
402 sx_out_off(bp, CD186x_GIVR, SX_ID); /* Set ID for this chip */
403 sx_out_off(bp, CD186x_GICR, 0); /* Clear all bits */
404 sx_out_off(bp, CD186x_PILR1, SX_ACK_MINT); /* Prio for modem intr */
405 sx_out_off(bp, CD186x_PILR2, SX_ACK_TINT); /* Prio for transmitter intr */
406 sx_out_off(bp, CD186x_PILR3, SX_ACK_RINT); /* Prio for receiver intr */
407 /* Set RegAckEn */
408 sx_out_off(bp, CD186x_SRCR, sx_in (bp, CD186x_SRCR) | SRCR_REGACKEN);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800409
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 /* Setting up prescaler. We need 4 ticks per 1 ms */
411 scaler = SX_OSCFREQ/SPECIALIX_TPS;
412
413 sx_out_off(bp, CD186x_PPRH, scaler >> 8);
414 sx_out_off(bp, CD186x_PPRL, scaler & 0xff);
415 spin_unlock_irqrestore(&bp->lock, flags);
416
417 if (!sx_set_irq (bp)) {
418 /* Figure out how to pass this along... */
419 printk (KERN_ERR "Cannot set irq to %d.\n", bp->irq);
420 rv = 0;
421 }
422
423 func_exit();
424 return rv;
425}
426
427
428static int read_cross_byte (struct specialix_board *bp, int reg, int bit)
429{
430 int i;
431 int t;
432 unsigned long flags;
433
434 spin_lock_irqsave(&bp->lock, flags);
435 for (i=0, t=0;i<8;i++) {
436 sx_out_off (bp, CD186x_CAR, i);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800437 if (sx_in_off (bp, reg) & bit)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438 t |= 1 << i;
439 }
440 spin_unlock_irqrestore(&bp->lock, flags);
441
442 return t;
443}
444
445
446#ifdef SPECIALIX_TIMER
447void missed_irq (unsigned long data)
448{
449 unsigned char irq;
450 unsigned long flags;
451 struct specialix_board *bp = (struct specialix_board *)data;
452
453 spin_lock_irqsave(&bp->lock, flags);
454 irq = sx_in ((struct specialix_board *)data, CD186x_SRSR) &
455 (SRSR_RREQint |
456 SRSR_TREQint |
457 SRSR_MREQint);
458 spin_unlock_irqrestore(&bp->lock, flags);
459 if (irq) {
460 printk (KERN_INFO "Missed interrupt... Calling int from timer. \n");
Jeff Garzikd61780c2005-10-30 15:01:51 -0800461 sx_interrupt (((struct specialix_board *)data)->irq,
Jiri Slabyd096f3e2007-02-12 00:52:30 -0800462 (void*)data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463 }
Jiri Slaby40565f12007-02-12 00:52:31 -0800464 mod_timer(&missed_irq_timer, jiffies + sx_poll);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465}
466#endif
467
468
469
470/* Main probing routine, also sets irq. */
471static int sx_probe(struct specialix_board *bp)
472{
473 unsigned char val1, val2;
474#if 0
475 int irqs = 0;
476 int retries;
477#endif
478 int rev;
479 int chip;
480
481 func_enter();
482
Jeff Garzikd61780c2005-10-30 15:01:51 -0800483 if (sx_request_io_range(bp)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 func_exit();
485 return 1;
486 }
487
488 /* Are the I/O ports here ? */
489 sx_out_off(bp, CD186x_PPRL, 0x5a);
490 short_pause ();
491 val1 = sx_in_off(bp, CD186x_PPRL);
492
493 sx_out_off(bp, CD186x_PPRL, 0xa5);
494 short_pause ();
495 val2 = sx_in_off(bp, CD186x_PPRL);
496
Jeff Garzikd61780c2005-10-30 15:01:51 -0800497
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498 if ((val1 != 0x5a) || (val2 != 0xa5)) {
499 printk(KERN_INFO "sx%d: specialix IO8+ Board at 0x%03x not found.\n",
500 board_No(bp), bp->base);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800501 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502 func_exit();
503 return 1;
504 }
505
Jeff Garzikd61780c2005-10-30 15:01:51 -0800506 /* Check the DSR lines that Specialix uses as board
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507 identification */
508 val1 = read_cross_byte (bp, CD186x_MSVR, MSVR_DSR);
509 val2 = read_cross_byte (bp, CD186x_MSVR, MSVR_RTS);
510 dprintk (SX_DEBUG_INIT, "sx%d: DSR lines are: %02x, rts lines are: %02x\n",
511 board_No(bp), val1, val2);
512
513 /* They managed to switch the bit order between the docs and
514 the IO8+ card. The new PCI card now conforms to old docs.
515 They changed the PCI docs to reflect the situation on the
516 old card. */
517 val2 = (bp->flags & SX_BOARD_IS_PCI)?0x4d : 0xb2;
518 if (val1 != val2) {
519 printk(KERN_INFO "sx%d: specialix IO8+ ID %02x at 0x%03x not found (%02x).\n",
520 board_No(bp), val2, bp->base, val1);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800521 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522 func_exit();
523 return 1;
524 }
525
526
527#if 0
528 /* It's time to find IRQ for this board */
529 for (retries = 0; retries < 5 && irqs <= 0; retries++) {
530 irqs = probe_irq_on();
531 sx_init_CD186x(bp); /* Reset CD186x chip */
532 sx_out(bp, CD186x_CAR, 2); /* Select port 2 */
533 sx_wait_CCR(bp);
534 sx_out(bp, CD186x_CCR, CCR_TXEN); /* Enable transmitter */
535 sx_out(bp, CD186x_IER, IER_TXRDY); /* Enable tx empty intr */
Jeff Garzikd61780c2005-10-30 15:01:51 -0800536 sx_long_delay(HZ/20);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537 irqs = probe_irq_off(irqs);
538
539 dprintk (SX_DEBUG_INIT, "SRSR = %02x, ", sx_in(bp, CD186x_SRSR));
540 dprintk (SX_DEBUG_INIT, "TRAR = %02x, ", sx_in(bp, CD186x_TRAR));
541 dprintk (SX_DEBUG_INIT, "GIVR = %02x, ", sx_in(bp, CD186x_GIVR));
542 dprintk (SX_DEBUG_INIT, "GICR = %02x, ", sx_in(bp, CD186x_GICR));
543 dprintk (SX_DEBUG_INIT, "\n");
544
545 /* Reset CD186x again */
546 if (!sx_init_CD186x(bp)) {
547 /* Hmmm. This is dead code anyway. */
548 }
549
550 dprintk (SX_DEBUG_INIT "val1 = %02x, val2 = %02x, val3 = %02x.\n",
Jeff Garzikd61780c2005-10-30 15:01:51 -0800551 val1, val2, val3);
552
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800554
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555#if 0
556 if (irqs <= 0) {
557 printk(KERN_ERR "sx%d: Can't find IRQ for specialix IO8+ board at 0x%03x.\n",
558 board_No(bp), bp->base);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800559 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560 func_exit();
561 return 1;
562 }
563#endif
564 printk (KERN_INFO "Started with irq=%d, but now have irq=%d.\n", bp->irq, irqs);
565 if (irqs > 0)
566 bp->irq = irqs;
567#endif
568 /* Reset CD186x again */
569 if (!sx_init_CD186x(bp)) {
Jeff Garzikd61780c2005-10-30 15:01:51 -0800570 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571 func_exit();
Jeff Garzikd61780c2005-10-30 15:01:51 -0800572 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573 }
574
575 sx_request_io_range(bp);
576 bp->flags |= SX_BOARD_PRESENT;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800577
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 /* Chip revcode pkgtype
579 GFRCR SRCR bit 7
580 CD180 rev B 0x81 0
581 CD180 rev C 0x82 0
582 CD1864 rev A 0x82 1
Jeff Garzikd61780c2005-10-30 15:01:51 -0800583 CD1865 rev A 0x83 1 -- Do not use!!! Does not work.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584 CD1865 rev B 0x84 1
585 -- Thanks to Gwen Wang, Cirrus Logic.
586 */
587
588 switch (sx_in_off(bp, CD186x_GFRCR)) {
589 case 0x82:chip = 1864;rev='A';break;
590 case 0x83:chip = 1865;rev='A';break;
591 case 0x84:chip = 1865;rev='B';break;
592 case 0x85:chip = 1865;rev='C';break; /* Does not exist at this time */
593 default:chip=-1;rev='x';
594 }
595
596 dprintk (SX_DEBUG_INIT, " GFCR = 0x%02x\n", sx_in_off(bp, CD186x_GFRCR) );
597
598#ifdef SPECIALIX_TIMER
Jiri Slaby40565f12007-02-12 00:52:31 -0800599 setup_timer(&missed_irq_timer, missed_irq, (unsigned long)bp);
600 mod_timer(&missed_irq_timer, jiffies + sx_poll);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601#endif
602
603 printk(KERN_INFO"sx%d: specialix IO8+ board detected at 0x%03x, IRQ %d, CD%d Rev. %c.\n",
604 board_No(bp),
605 bp->base, bp->irq,
606 chip, rev);
607
608 func_exit();
609 return 0;
610}
611
Jeff Garzikd61780c2005-10-30 15:01:51 -0800612/*
613 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614 * Interrupt processing routines.
615 * */
616
617static inline void sx_mark_event(struct specialix_port * port, int event)
618{
619 func_enter();
620
621 set_bit(event, &port->event);
622 schedule_work(&port->tqueue);
623
624 func_exit();
625}
626
627
628static inline struct specialix_port * sx_get_port(struct specialix_board * bp,
629 unsigned char const * what)
630{
631 unsigned char channel;
632 struct specialix_port * port = NULL;
633
634 channel = sx_in(bp, CD186x_GICR) >> GICR_CHAN_OFF;
635 dprintk (SX_DEBUG_CHAN, "channel: %d\n", channel);
636 if (channel < CD186x_NCH) {
637 port = &sx_port[board_No(bp) * SX_NPORT + channel];
638 dprintk (SX_DEBUG_CHAN, "port: %d %p flags: 0x%x\n",board_No(bp) * SX_NPORT + channel, port, port->flags & ASYNC_INITIALIZED);
639
640 if (port->flags & ASYNC_INITIALIZED) {
641 dprintk (SX_DEBUG_CHAN, "port: %d %p\n", channel, port);
642 func_exit();
643 return port;
644 }
645 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800646 printk(KERN_INFO "sx%d: %s interrupt from invalid port %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647 board_No(bp), what, channel);
648 return NULL;
649}
650
651
652static inline void sx_receive_exc(struct specialix_board * bp)
653{
654 struct specialix_port *port;
655 struct tty_struct *tty;
656 unsigned char status;
Alan Cox33f0f882006-01-09 20:54:13 -0800657 unsigned char ch, flag;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658
659 func_enter();
660
661 port = sx_get_port(bp, "Receive");
662 if (!port) {
663 dprintk (SX_DEBUG_RX, "Hmm, couldn't find port.\n");
664 func_exit();
665 return;
666 }
667 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800668
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669 status = sx_in(bp, CD186x_RCSR);
670
671 dprintk (SX_DEBUG_RX, "status: 0x%x\n", status);
672 if (status & RCSR_OE) {
673 port->overrun++;
674 dprintk(SX_DEBUG_FIFO, "sx%d: port %d: Overrun. Total %ld overruns.\n",
675 board_No(bp), port_No(port), port->overrun);
676 }
677 status &= port->mark_mask;
678
679 /* This flip buffer check needs to be below the reading of the
680 status register to reset the chip's IRQ.... */
Alan Cox33f0f882006-01-09 20:54:13 -0800681 if (tty_buffer_request_room(tty, 1) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682 dprintk(SX_DEBUG_FIFO, "sx%d: port %d: Working around flip buffer overflow.\n",
683 board_No(bp), port_No(port));
684 func_exit();
685 return;
686 }
687
688 ch = sx_in(bp, CD186x_RDR);
689 if (!status) {
690 func_exit();
691 return;
692 }
693 if (status & RCSR_TOUT) {
Jeff Garzikd61780c2005-10-30 15:01:51 -0800694 printk(KERN_INFO "sx%d: port %d: Receiver timeout. Hardware problems ?\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695 board_No(bp), port_No(port));
696 func_exit();
697 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800698
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699 } else if (status & RCSR_BREAK) {
700 dprintk(SX_DEBUG_RX, "sx%d: port %d: Handling break...\n",
701 board_No(bp), port_No(port));
Alan Cox33f0f882006-01-09 20:54:13 -0800702 flag = TTY_BREAK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 if (port->flags & ASYNC_SAK)
704 do_SAK(tty);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800705
706 } else if (status & RCSR_PE)
Alan Cox33f0f882006-01-09 20:54:13 -0800707 flag = TTY_PARITY;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800708
709 else if (status & RCSR_FE)
Alan Cox33f0f882006-01-09 20:54:13 -0800710 flag = TTY_FRAME;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800711
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712 else if (status & RCSR_OE)
Alan Cox33f0f882006-01-09 20:54:13 -0800713 flag = TTY_OVERRUN;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800714
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715 else
Alan Cox33f0f882006-01-09 20:54:13 -0800716 flag = TTY_NORMAL;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800717
Alan Cox33f0f882006-01-09 20:54:13 -0800718 if(tty_insert_flip_char(tty, ch, flag))
719 tty_flip_buffer_push(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720 func_exit();
721}
722
723
724static inline void sx_receive(struct specialix_board * bp)
725{
726 struct specialix_port *port;
727 struct tty_struct *tty;
728 unsigned char count;
729
730 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -0800731
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732 if (!(port = sx_get_port(bp, "Receive"))) {
733 dprintk (SX_DEBUG_RX, "Hmm, couldn't find port.\n");
734 func_exit();
735 return;
736 }
737 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800738
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739 count = sx_in(bp, CD186x_RDCR);
740 dprintk (SX_DEBUG_RX, "port: %p: count: %d\n", port, count);
741 port->hits[count > 8 ? 9 : count]++;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800742
Alan Cox33f0f882006-01-09 20:54:13 -0800743 tty_buffer_request_room(tty, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744
Alan Cox33f0f882006-01-09 20:54:13 -0800745 while (count--)
746 tty_insert_flip_char(tty, sx_in(bp, CD186x_RDR), TTY_NORMAL);
747 tty_flip_buffer_push(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748 func_exit();
749}
750
751
752static inline void sx_transmit(struct specialix_board * bp)
753{
754 struct specialix_port *port;
755 struct tty_struct *tty;
756 unsigned char count;
757
758 func_enter();
759 if (!(port = sx_get_port(bp, "Transmit"))) {
760 func_exit();
761 return;
762 }
763 dprintk (SX_DEBUG_TX, "port: %p\n", port);
764 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800765
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766 if (port->IER & IER_TXEMPTY) {
767 /* FIFO drained */
768 sx_out(bp, CD186x_CAR, port_No(port));
769 port->IER &= ~IER_TXEMPTY;
770 sx_out(bp, CD186x_IER, port->IER);
771 func_exit();
772 return;
773 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800774
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 if ((port->xmit_cnt <= 0 && !port->break_length)
776 || tty->stopped || tty->hw_stopped) {
777 sx_out(bp, CD186x_CAR, port_No(port));
778 port->IER &= ~IER_TXRDY;
779 sx_out(bp, CD186x_IER, port->IER);
780 func_exit();
781 return;
782 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800783
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784 if (port->break_length) {
785 if (port->break_length > 0) {
786 if (port->COR2 & COR2_ETC) {
787 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
788 sx_out(bp, CD186x_TDR, CD186x_C_SBRK);
789 port->COR2 &= ~COR2_ETC;
790 }
791 count = min_t(int, port->break_length, 0xff);
792 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
793 sx_out(bp, CD186x_TDR, CD186x_C_DELAY);
794 sx_out(bp, CD186x_TDR, count);
795 if (!(port->break_length -= count))
796 port->break_length--;
797 } else {
798 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
799 sx_out(bp, CD186x_TDR, CD186x_C_EBRK);
800 sx_out(bp, CD186x_COR2, port->COR2);
801 sx_wait_CCR(bp);
802 sx_out(bp, CD186x_CCR, CCR_CORCHG2);
803 port->break_length = 0;
804 }
805
806 func_exit();
807 return;
808 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800809
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810 count = CD186x_NFIFO;
811 do {
812 sx_out(bp, CD186x_TDR, port->xmit_buf[port->xmit_tail++]);
813 port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1);
814 if (--port->xmit_cnt <= 0)
815 break;
816 } while (--count > 0);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800817
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818 if (port->xmit_cnt <= 0) {
819 sx_out(bp, CD186x_CAR, port_No(port));
820 port->IER &= ~IER_TXRDY;
821 sx_out(bp, CD186x_IER, port->IER);
822 }
823 if (port->xmit_cnt <= port->wakeup_chars)
824 sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);
825
826 func_exit();
827}
828
829
830static inline void sx_check_modem(struct specialix_board * bp)
831{
832 struct specialix_port *port;
833 struct tty_struct *tty;
834 unsigned char mcr;
835 int msvr_cd;
836
837 dprintk (SX_DEBUG_SIGNALS, "Modem intr. ");
838 if (!(port = sx_get_port(bp, "Modem")))
839 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800840
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800842
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843 mcr = sx_in(bp, CD186x_MCR);
844 printk ("mcr = %02x.\n", mcr);
845
846 if ((mcr & MCR_CDCHG)) {
847 dprintk (SX_DEBUG_SIGNALS, "CD just changed... ");
848 msvr_cd = sx_in(bp, CD186x_MSVR) & MSVR_CD;
849 if (msvr_cd) {
850 dprintk (SX_DEBUG_SIGNALS, "Waking up guys in open.\n");
851 wake_up_interruptible(&port->open_wait);
852 } else {
853 dprintk (SX_DEBUG_SIGNALS, "Sending HUP.\n");
854 schedule_work(&port->tqueue_hangup);
855 }
856 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800857
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
859 if (mcr & MCR_CTSCHG) {
860 if (sx_in(bp, CD186x_MSVR) & MSVR_CTS) {
861 tty->hw_stopped = 0;
862 port->IER |= IER_TXRDY;
863 if (port->xmit_cnt <= port->wakeup_chars)
864 sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);
865 } else {
866 tty->hw_stopped = 1;
867 port->IER &= ~IER_TXRDY;
868 }
869 sx_out(bp, CD186x_IER, port->IER);
870 }
871 if (mcr & MCR_DSSXHG) {
872 if (sx_in(bp, CD186x_MSVR) & MSVR_DSR) {
873 tty->hw_stopped = 0;
874 port->IER |= IER_TXRDY;
875 if (port->xmit_cnt <= port->wakeup_chars)
876 sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);
877 } else {
878 tty->hw_stopped = 1;
879 port->IER &= ~IER_TXRDY;
880 }
881 sx_out(bp, CD186x_IER, port->IER);
882 }
883#endif /* SPECIALIX_BRAIN_DAMAGED_CTS */
Jeff Garzikd61780c2005-10-30 15:01:51 -0800884
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885 /* Clear change bits */
886 sx_out(bp, CD186x_MCR, 0);
887}
888
889
890/* The main interrupt processing routine */
David Howells7d12e782006-10-05 14:55:46 +0100891static irqreturn_t sx_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892{
893 unsigned char status;
894 unsigned char ack;
895 struct specialix_board *bp;
896 unsigned long loop = 0;
897 int saved_reg;
898 unsigned long flags;
899
900 func_enter();
901
902 bp = dev_id;
903 spin_lock_irqsave(&bp->lock, flags);
904
905 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 -0400906 if (!(bp->flags & SX_BOARD_ACTIVE)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907 dprintk (SX_DEBUG_IRQ, "sx: False interrupt. irq %d.\n", irq);
908 spin_unlock_irqrestore(&bp->lock, flags);
909 func_exit();
910 return IRQ_NONE;
911 }
912
913 saved_reg = bp->reg;
914
915 while ((++loop < 16) && (status = (sx_in(bp, CD186x_SRSR) &
916 (SRSR_RREQint |
917 SRSR_TREQint |
Jeff Garzikd61780c2005-10-30 15:01:51 -0800918 SRSR_MREQint)))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700919 if (status & SRSR_RREQint) {
920 ack = sx_in(bp, CD186x_RRAR);
921
922 if (ack == (SX_ID | GIVR_IT_RCV))
923 sx_receive(bp);
924 else if (ack == (SX_ID | GIVR_IT_REXC))
925 sx_receive_exc(bp);
926 else
927 printk(KERN_ERR "sx%d: status: 0x%x Bad receive ack 0x%02x.\n",
928 board_No(bp), status, ack);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800929
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930 } else if (status & SRSR_TREQint) {
931 ack = sx_in(bp, CD186x_TRAR);
932
933 if (ack == (SX_ID | GIVR_IT_TX))
934 sx_transmit(bp);
935 else
936 printk(KERN_ERR "sx%d: status: 0x%x Bad transmit ack 0x%02x. port: %d\n",
937 board_No(bp), status, ack, port_No (sx_get_port (bp, "Int")));
938 } else if (status & SRSR_MREQint) {
939 ack = sx_in(bp, CD186x_MRAR);
940
Jeff Garzikd61780c2005-10-30 15:01:51 -0800941 if (ack == (SX_ID | GIVR_IT_MODEM))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942 sx_check_modem(bp);
943 else
944 printk(KERN_ERR "sx%d: status: 0x%x Bad modem ack 0x%02x.\n",
945 board_No(bp), status, ack);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800946
947 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948
949 sx_out(bp, CD186x_EOIR, 0); /* Mark end of interrupt */
950 }
951 bp->reg = saved_reg;
952 outb (bp->reg, bp->base + SX_ADDR_REG);
953 spin_unlock_irqrestore(&bp->lock, flags);
954 func_exit();
955 return IRQ_HANDLED;
956}
957
958
959/*
960 * Routines for open & close processing.
961 */
962
963static void turn_ints_off (struct specialix_board *bp)
964{
965 unsigned long flags;
966
967 func_enter();
968 if (bp->flags & SX_BOARD_IS_PCI) {
969 /* This was intended for enabeling the interrupt on the
970 * PCI card. However it seems that it's already enabled
971 * and as PCI interrupts can be shared, there is no real
972 * reason to have to turn it off. */
973 }
974
975 spin_lock_irqsave(&bp->lock, flags);
976 (void) sx_in_off (bp, 0); /* Turn off interrupts. */
977 spin_unlock_irqrestore(&bp->lock, flags);
978
979 func_exit();
980}
981
982static void turn_ints_on (struct specialix_board *bp)
983{
984 unsigned long flags;
985
986 func_enter();
987
988 if (bp->flags & SX_BOARD_IS_PCI) {
989 /* play with the PCI chip. See comment above. */
990 }
991 spin_lock_irqsave(&bp->lock, flags);
992 (void) sx_in (bp, 0); /* Turn ON interrupts. */
993 spin_unlock_irqrestore(&bp->lock, flags);
994
995 func_exit();
996}
997
998
999/* Called with disabled interrupts */
1000static inline int sx_setup_board(struct specialix_board * bp)
1001{
1002 int error;
1003
Jeff Garzikd61780c2005-10-30 15:01:51 -08001004 if (bp->flags & SX_BOARD_ACTIVE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005 return 0;
1006
1007 if (bp->flags & SX_BOARD_IS_PCI)
Thomas Gleixner0f2ed4c2006-07-01 19:29:33 -07001008 error = request_irq(bp->irq, sx_interrupt, IRQF_DISABLED | IRQF_SHARED, "specialix IO8+", bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001009 else
Thomas Gleixner0f2ed4c2006-07-01 19:29:33 -07001010 error = request_irq(bp->irq, sx_interrupt, IRQF_DISABLED, "specialix IO8+", bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011
Jeff Garzikd61780c2005-10-30 15:01:51 -08001012 if (error)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013 return error;
1014
1015 turn_ints_on (bp);
1016 bp->flags |= SX_BOARD_ACTIVE;
1017
1018 return 0;
1019}
1020
1021
1022/* Called with disabled interrupts */
1023static inline void sx_shutdown_board(struct specialix_board *bp)
1024{
1025 func_enter();
1026
1027 if (!(bp->flags & SX_BOARD_ACTIVE)) {
1028 func_exit();
1029 return;
1030 }
1031
1032 bp->flags &= ~SX_BOARD_ACTIVE;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001033
Linus Torvalds1da177e2005-04-16 15:20:36 -07001034 dprintk (SX_DEBUG_IRQ, "Freeing IRQ%d for board %d.\n",
1035 bp->irq, board_No (bp));
1036 free_irq(bp->irq, bp);
1037
1038 turn_ints_off (bp);
1039
1040
1041 func_exit();
1042}
1043
1044
1045/*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001046 * Setting up port characteristics.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047 * Must be called with disabled interrupts
1048 */
1049static void sx_change_speed(struct specialix_board *bp, struct specialix_port *port)
1050{
1051 struct tty_struct *tty;
1052 unsigned long baud;
1053 long tmp;
1054 unsigned char cor1 = 0, cor3 = 0;
1055 unsigned char mcor1 = 0, mcor2 = 0;
1056 static unsigned long again;
1057 unsigned long flags;
1058
1059 func_enter();
1060
1061 if (!(tty = port->tty) || !tty->termios) {
1062 func_exit();
1063 return;
1064 }
1065
1066 port->IER = 0;
1067 port->COR2 = 0;
1068 /* Select port on the board */
1069 spin_lock_irqsave(&bp->lock, flags);
1070 sx_out(bp, CD186x_CAR, port_No(port));
1071
1072 /* The Specialix board doens't implement the RTS lines.
1073 They are used to set the IRQ level. Don't touch them. */
1074 if (SX_CRTSCTS(tty))
1075 port->MSVR = MSVR_DTR | (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
1076 else
1077 port->MSVR = (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
1078 spin_unlock_irqrestore(&bp->lock, flags);
1079 dprintk (SX_DEBUG_TERMIOS, "sx: got MSVR=%02x.\n", port->MSVR);
Alan Cox67cc0162006-09-29 02:01:39 -07001080 baud = tty_get_baud_rate(tty);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001081
Alan Cox67cc0162006-09-29 02:01:39 -07001082 if (baud == 38400) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083 if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
Adrian Bunka4bb2cf2006-10-17 00:10:00 -07001084 baud = 57600;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085 if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
Adrian Bunka4bb2cf2006-10-17 00:10:00 -07001086 baud = 115200;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001088
Alan Cox67cc0162006-09-29 02:01:39 -07001089 if (!baud) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090 /* Drop DTR & exit */
1091 dprintk (SX_DEBUG_TERMIOS, "Dropping DTR... Hmm....\n");
1092 if (!SX_CRTSCTS (tty)) {
1093 port -> MSVR &= ~ MSVR_DTR;
1094 spin_lock_irqsave(&bp->lock, flags);
1095 sx_out(bp, CD186x_MSVR, port->MSVR );
1096 spin_unlock_irqrestore(&bp->lock, flags);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001097 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098 else
1099 dprintk (SX_DEBUG_TERMIOS, "Can't drop DTR: no DTR.\n");
1100 return;
1101 } else {
1102 /* Set DTR on */
1103 if (!SX_CRTSCTS (tty)) {
1104 port ->MSVR |= MSVR_DTR;
1105 }
1106 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001107
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108 /*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001109 * Now we must calculate some speed depended things
Linus Torvalds1da177e2005-04-16 15:20:36 -07001110 */
1111
1112 /* Set baud rate for port */
1113 tmp = port->custom_divisor ;
1114 if ( tmp )
1115 printk (KERN_INFO "sx%d: Using custom baud rate divisor %ld. \n"
1116 "This is an untested option, please be carefull.\n",
1117 port_No (port), tmp);
1118 else
Alan Cox67cc0162006-09-29 02:01:39 -07001119 tmp = (((SX_OSCFREQ + baud/2) / baud +
Linus Torvalds1da177e2005-04-16 15:20:36 -07001120 CD186x_TPC/2) / CD186x_TPC);
1121
Jeff Garzikd61780c2005-10-30 15:01:51 -08001122 if ((tmp < 0x10) && time_before(again, jiffies)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001123 again = jiffies + HZ * 60;
1124 /* Page 48 of version 2.0 of the CL-CD1865 databook */
1125 if (tmp >= 12) {
1126 printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
1127 "Performance degradation is possible.\n"
1128 "Read specialix.txt for more info.\n",
1129 port_No (port), tmp);
1130 } else {
1131 printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
1132 "Warning: overstressing Cirrus chip. "
1133 "This might not work.\n"
Jeff Garzikd61780c2005-10-30 15:01:51 -08001134 "Read specialix.txt for more info.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135 port_No (port), tmp);
1136 }
1137 }
1138 spin_lock_irqsave(&bp->lock, flags);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001139 sx_out(bp, CD186x_RBPRH, (tmp >> 8) & 0xff);
1140 sx_out(bp, CD186x_TBPRH, (tmp >> 8) & 0xff);
1141 sx_out(bp, CD186x_RBPRL, tmp & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142 sx_out(bp, CD186x_TBPRL, tmp & 0xff);
1143 spin_unlock_irqrestore(&bp->lock, flags);
Adrian Bunka4bb2cf2006-10-17 00:10:00 -07001144 if (port->custom_divisor)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001145 baud = (SX_OSCFREQ + port->custom_divisor/2) / port->custom_divisor;
Adrian Bunka4bb2cf2006-10-17 00:10:00 -07001146 baud = (baud + 5) / 10; /* Estimated CPS */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147
1148 /* Two timer ticks seems enough to wakeup something like SLIP driver */
Jeff Garzikd61780c2005-10-30 15:01:51 -08001149 tmp = ((baud + HZ/2) / HZ) * 2 - CD186x_NFIFO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150 port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ?
1151 SERIAL_XMIT_SIZE - 1 : tmp);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001152
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153 /* Receiver timeout will be transmission time for 1.5 chars */
1154 tmp = (SPECIALIX_TPS + SPECIALIX_TPS/2 + baud/2) / baud;
1155 tmp = (tmp > 0xff) ? 0xff : tmp;
1156 spin_lock_irqsave(&bp->lock, flags);
1157 sx_out(bp, CD186x_RTPR, tmp);
1158 spin_unlock_irqrestore(&bp->lock, flags);
1159 switch (C_CSIZE(tty)) {
1160 case CS5:
1161 cor1 |= COR1_5BITS;
1162 break;
1163 case CS6:
1164 cor1 |= COR1_6BITS;
1165 break;
1166 case CS7:
1167 cor1 |= COR1_7BITS;
1168 break;
1169 case CS8:
1170 cor1 |= COR1_8BITS;
1171 break;
1172 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001173
1174 if (C_CSTOPB(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175 cor1 |= COR1_2SB;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001176
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177 cor1 |= COR1_IGNORE;
1178 if (C_PARENB(tty)) {
1179 cor1 |= COR1_NORMPAR;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001180 if (C_PARODD(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001181 cor1 |= COR1_ODDP;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001182 if (I_INPCK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001183 cor1 &= ~COR1_IGNORE;
1184 }
1185 /* Set marking of some errors */
1186 port->mark_mask = RCSR_OE | RCSR_TOUT;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001187 if (I_INPCK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001188 port->mark_mask |= RCSR_FE | RCSR_PE;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001189 if (I_BRKINT(tty) || I_PARMRK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001190 port->mark_mask |= RCSR_BREAK;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001191 if (I_IGNPAR(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001192 port->mark_mask &= ~(RCSR_FE | RCSR_PE);
1193 if (I_IGNBRK(tty)) {
1194 port->mark_mask &= ~RCSR_BREAK;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001195 if (I_IGNPAR(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196 /* Real raw mode. Ignore all */
1197 port->mark_mask &= ~RCSR_OE;
1198 }
1199 /* Enable Hardware Flow Control */
1200 if (C_CRTSCTS(tty)) {
1201#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
1202 port->IER |= IER_DSR | IER_CTS;
1203 mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD;
1204 mcor2 |= MCOR2_DSROD | MCOR2_CTSOD;
1205 spin_lock_irqsave(&bp->lock, flags);
1206 tty->hw_stopped = !(sx_in(bp, CD186x_MSVR) & (MSVR_CTS|MSVR_DSR));
1207 spin_unlock_irqrestore(&bp->lock, flags);
1208#else
Jeff Garzikd61780c2005-10-30 15:01:51 -08001209 port->COR2 |= COR2_CTSAE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210#endif
1211 }
1212 /* Enable Software Flow Control. FIXME: I'm not sure about this */
1213 /* Some people reported that it works, but I still doubt it */
1214 if (I_IXON(tty)) {
1215 port->COR2 |= COR2_TXIBE;
1216 cor3 |= (COR3_FCT | COR3_SCDE);
1217 if (I_IXANY(tty))
1218 port->COR2 |= COR2_IXM;
1219 spin_lock_irqsave(&bp->lock, flags);
1220 sx_out(bp, CD186x_SCHR1, START_CHAR(tty));
1221 sx_out(bp, CD186x_SCHR2, STOP_CHAR(tty));
1222 sx_out(bp, CD186x_SCHR3, START_CHAR(tty));
1223 sx_out(bp, CD186x_SCHR4, STOP_CHAR(tty));
1224 spin_unlock_irqrestore(&bp->lock, flags);
1225 }
1226 if (!C_CLOCAL(tty)) {
1227 /* Enable CD check */
1228 port->IER |= IER_CD;
1229 mcor1 |= MCOR1_CDZD;
1230 mcor2 |= MCOR2_CDOD;
1231 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001232
1233 if (C_CREAD(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001234 /* Enable receiver */
1235 port->IER |= IER_RXD;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001236
Linus Torvalds1da177e2005-04-16 15:20:36 -07001237 /* Set input FIFO size (1-8 bytes) */
1238 cor3 |= sx_rxfifo;
1239 /* Setting up CD186x channel registers */
1240 spin_lock_irqsave(&bp->lock, flags);
1241 sx_out(bp, CD186x_COR1, cor1);
1242 sx_out(bp, CD186x_COR2, port->COR2);
1243 sx_out(bp, CD186x_COR3, cor3);
1244 spin_unlock_irqrestore(&bp->lock, flags);
1245 /* Make CD186x know about registers change */
1246 sx_wait_CCR(bp);
1247 spin_lock_irqsave(&bp->lock, flags);
1248 sx_out(bp, CD186x_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3);
1249 /* Setting up modem option registers */
1250 dprintk (SX_DEBUG_TERMIOS, "Mcor1 = %02x, mcor2 = %02x.\n", mcor1, mcor2);
1251 sx_out(bp, CD186x_MCOR1, mcor1);
1252 sx_out(bp, CD186x_MCOR2, mcor2);
1253 spin_unlock_irqrestore(&bp->lock, flags);
1254 /* Enable CD186x transmitter & receiver */
1255 sx_wait_CCR(bp);
1256 spin_lock_irqsave(&bp->lock, flags);
1257 sx_out(bp, CD186x_CCR, CCR_TXEN | CCR_RXEN);
1258 /* Enable interrupts */
1259 sx_out(bp, CD186x_IER, port->IER);
1260 /* And finally set the modem lines... */
1261 sx_out(bp, CD186x_MSVR, port->MSVR);
1262 spin_unlock_irqrestore(&bp->lock, flags);
1263
1264 func_exit();
1265}
1266
1267
1268/* Must be called with interrupts enabled */
1269static int sx_setup_port(struct specialix_board *bp, struct specialix_port *port)
1270{
1271 unsigned long flags;
1272
1273 func_enter();
1274
1275 if (port->flags & ASYNC_INITIALIZED) {
1276 func_exit();
1277 return 0;
1278 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001279
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280 if (!port->xmit_buf) {
1281 /* We may sleep in get_zeroed_page() */
1282 unsigned long tmp;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001283
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284 if (!(tmp = get_zeroed_page(GFP_KERNEL))) {
1285 func_exit();
1286 return -ENOMEM;
1287 }
1288
1289 if (port->xmit_buf) {
1290 free_page(tmp);
1291 func_exit();
1292 return -ERESTARTSYS;
1293 }
1294 port->xmit_buf = (unsigned char *) tmp;
1295 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001296
Linus Torvalds1da177e2005-04-16 15:20:36 -07001297 spin_lock_irqsave(&port->lock, flags);
1298
Jeff Garzikd61780c2005-10-30 15:01:51 -08001299 if (port->tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001300 clear_bit(TTY_IO_ERROR, &port->tty->flags);
1301
1302 port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
1303 sx_change_speed(bp, port);
1304 port->flags |= ASYNC_INITIALIZED;
1305
1306 spin_unlock_irqrestore(&port->lock, flags);
1307
Jeff Garzikd61780c2005-10-30 15:01:51 -08001308
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309 func_exit();
1310 return 0;
1311}
1312
1313
1314/* Must be called with interrupts disabled */
1315static void sx_shutdown_port(struct specialix_board *bp, struct specialix_port *port)
1316{
1317 struct tty_struct *tty;
1318 int i;
1319 unsigned long flags;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001320
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321 func_enter();
1322
1323 if (!(port->flags & ASYNC_INITIALIZED)) {
1324 func_exit();
1325 return;
1326 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001327
Linus Torvalds1da177e2005-04-16 15:20:36 -07001328 if (sx_debug & SX_DEBUG_FIFO) {
1329 dprintk(SX_DEBUG_FIFO, "sx%d: port %d: %ld overruns, FIFO hits [ ",
1330 board_No(bp), port_No(port), port->overrun);
1331 for (i = 0; i < 10; i++) {
1332 dprintk(SX_DEBUG_FIFO, "%ld ", port->hits[i]);
1333 }
1334 dprintk(SX_DEBUG_FIFO, "].\n");
1335 }
1336
1337 if (port->xmit_buf) {
1338 free_page((unsigned long) port->xmit_buf);
1339 port->xmit_buf = NULL;
1340 }
1341
1342 /* Select port */
1343 spin_lock_irqsave(&bp->lock, flags);
1344 sx_out(bp, CD186x_CAR, port_No(port));
1345
1346 if (!(tty = port->tty) || C_HUPCL(tty)) {
1347 /* Drop DTR */
1348 sx_out(bp, CD186x_MSVDTR, 0);
1349 }
1350 spin_unlock_irqrestore(&bp->lock, flags);
1351 /* Reset port */
1352 sx_wait_CCR(bp);
1353 spin_lock_irqsave(&bp->lock, flags);
1354 sx_out(bp, CD186x_CCR, CCR_SOFTRESET);
1355 /* Disable all interrupts from this port */
1356 port->IER = 0;
1357 sx_out(bp, CD186x_IER, port->IER);
1358 spin_unlock_irqrestore(&bp->lock, flags);
1359 if (tty)
1360 set_bit(TTY_IO_ERROR, &tty->flags);
1361 port->flags &= ~ASYNC_INITIALIZED;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001362
1363 if (!bp->count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364 sx_shutdown_board(bp);
1365 func_exit();
1366}
1367
Jeff Garzikd61780c2005-10-30 15:01:51 -08001368
Linus Torvalds1da177e2005-04-16 15:20:36 -07001369static int block_til_ready(struct tty_struct *tty, struct file * filp,
1370 struct specialix_port *port)
1371{
1372 DECLARE_WAITQUEUE(wait, current);
1373 struct specialix_board *bp = port_Board(port);
1374 int retval;
1375 int do_clocal = 0;
1376 int CD;
1377 unsigned long flags;
1378
1379 func_enter();
1380
1381 /*
1382 * If the device is in the middle of being closed, then block
1383 * until it's done, and then try again.
1384 */
1385 if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {
1386 interruptible_sleep_on(&port->close_wait);
1387 if (port->flags & ASYNC_HUP_NOTIFY) {
1388 func_exit();
1389 return -EAGAIN;
1390 } else {
1391 func_exit();
1392 return -ERESTARTSYS;
1393 }
1394 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001395
Linus Torvalds1da177e2005-04-16 15:20:36 -07001396 /*
1397 * If non-blocking mode is set, or the port is not enabled,
1398 * then make the check up front and then exit.
1399 */
1400 if ((filp->f_flags & O_NONBLOCK) ||
1401 (tty->flags & (1 << TTY_IO_ERROR))) {
1402 port->flags |= ASYNC_NORMAL_ACTIVE;
1403 func_exit();
1404 return 0;
1405 }
1406
1407 if (C_CLOCAL(tty))
1408 do_clocal = 1;
1409
1410 /*
1411 * Block waiting for the carrier detect and the line to become
1412 * free (i.e., not in use by the callout). While we are in
1413 * this loop, info->count is dropped by one, so that
1414 * rs_close() knows when to free things. We restore it upon
1415 * exit, either normal or abnormal.
1416 */
1417 retval = 0;
1418 add_wait_queue(&port->open_wait, &wait);
1419 spin_lock_irqsave(&port->lock, flags);
1420 if (!tty_hung_up_p(filp)) {
1421 port->count--;
1422 }
1423 spin_unlock_irqrestore(&port->lock, flags);
1424 port->blocked_open++;
1425 while (1) {
1426 spin_lock_irqsave(&bp->lock, flags);
1427 sx_out(bp, CD186x_CAR, port_No(port));
1428 CD = sx_in(bp, CD186x_MSVR) & MSVR_CD;
1429 if (SX_CRTSCTS (tty)) {
1430 /* Activate RTS */
1431 port->MSVR |= MSVR_DTR; /* WTF? */
1432 sx_out (bp, CD186x_MSVR, port->MSVR);
1433 } else {
1434 /* Activate DTR */
1435 port->MSVR |= MSVR_DTR;
1436 sx_out (bp, CD186x_MSVR, port->MSVR);
1437 }
1438 spin_unlock_irqrestore(&bp->lock, flags);
1439 set_current_state(TASK_INTERRUPTIBLE);
1440 if (tty_hung_up_p(filp) ||
1441 !(port->flags & ASYNC_INITIALIZED)) {
1442 if (port->flags & ASYNC_HUP_NOTIFY)
1443 retval = -EAGAIN;
1444 else
Jeff Garzikd61780c2005-10-30 15:01:51 -08001445 retval = -ERESTARTSYS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001446 break;
1447 }
1448 if (!(port->flags & ASYNC_CLOSING) &&
1449 (do_clocal || CD))
1450 break;
1451 if (signal_pending(current)) {
1452 retval = -ERESTARTSYS;
1453 break;
1454 }
1455 schedule();
1456 }
1457
1458 set_current_state(TASK_RUNNING);
1459 remove_wait_queue(&port->open_wait, &wait);
1460 spin_lock_irqsave(&port->lock, flags);
1461 if (!tty_hung_up_p(filp)) {
1462 port->count++;
1463 }
1464 port->blocked_open--;
1465 spin_unlock_irqrestore(&port->lock, flags);
1466 if (retval) {
1467 func_exit();
1468 return retval;
1469 }
1470
1471 port->flags |= ASYNC_NORMAL_ACTIVE;
1472 func_exit();
1473 return 0;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001474}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001475
1476
1477static int sx_open(struct tty_struct * tty, struct file * filp)
1478{
1479 int board;
1480 int error;
1481 struct specialix_port * port;
1482 struct specialix_board * bp;
1483 int i;
1484 unsigned long flags;
1485
1486 func_enter();
1487
1488 board = SX_BOARD(tty->index);
1489
1490 if (board >= SX_NBOARD || !(sx_board[board].flags & SX_BOARD_PRESENT)) {
1491 func_exit();
1492 return -ENODEV;
1493 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001494
Linus Torvalds1da177e2005-04-16 15:20:36 -07001495 bp = &sx_board[board];
1496 port = sx_port + board * SX_NPORT + SX_PORT(tty->index);
1497 port->overrun = 0;
1498 for (i = 0; i < 10; i++)
1499 port->hits[i]=0;
1500
1501 dprintk (SX_DEBUG_OPEN, "Board = %d, bp = %p, port = %p, portno = %d.\n",
1502 board, bp, port, SX_PORT(tty->index));
1503
1504 if (sx_paranoia_check(port, tty->name, "sx_open")) {
1505 func_enter();
1506 return -ENODEV;
1507 }
1508
1509 if ((error = sx_setup_board(bp))) {
1510 func_exit();
1511 return error;
1512 }
1513
1514 spin_lock_irqsave(&bp->lock, flags);
1515 port->count++;
1516 bp->count++;
1517 tty->driver_data = port;
1518 port->tty = tty;
1519 spin_unlock_irqrestore(&bp->lock, flags);
1520
1521 if ((error = sx_setup_port(bp, port))) {
1522 func_enter();
1523 return error;
1524 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001525
Linus Torvalds1da177e2005-04-16 15:20:36 -07001526 if ((error = block_til_ready(tty, filp, port))) {
1527 func_enter();
1528 return error;
1529 }
1530
1531 func_exit();
1532 return 0;
1533}
1534
1535
1536static void sx_close(struct tty_struct * tty, struct file * filp)
1537{
1538 struct specialix_port *port = (struct specialix_port *) tty->driver_data;
1539 struct specialix_board *bp;
1540 unsigned long flags;
1541 unsigned long timeout;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001542
Linus Torvalds1da177e2005-04-16 15:20:36 -07001543 func_enter();
1544 if (!port || sx_paranoia_check(port, tty->name, "close")) {
1545 func_exit();
1546 return;
1547 }
1548 spin_lock_irqsave(&port->lock, flags);
1549
1550 if (tty_hung_up_p(filp)) {
1551 spin_unlock_irqrestore(&port->lock, flags);
1552 func_exit();
1553 return;
1554 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001555
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556 bp = port_Board(port);
1557 if ((tty->count == 1) && (port->count != 1)) {
1558 printk(KERN_ERR "sx%d: sx_close: bad port count;"
1559 " tty->count is 1, port count is %d\n",
1560 board_No(bp), port->count);
1561 port->count = 1;
1562 }
1563
1564 if (port->count > 1) {
1565 port->count--;
1566 bp->count--;
1567
1568 spin_unlock_irqrestore(&port->lock, flags);
1569
1570 func_exit();
1571 return;
1572 }
1573 port->flags |= ASYNC_CLOSING;
1574 /*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001575 * Now we wait for the transmit buffer to clear; and we notify
Linus Torvalds1da177e2005-04-16 15:20:36 -07001576 * the line discipline to only process XON/XOFF characters.
1577 */
1578 tty->closing = 1;
1579 spin_unlock_irqrestore(&port->lock, flags);
1580 dprintk (SX_DEBUG_OPEN, "Closing\n");
1581 if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) {
1582 tty_wait_until_sent(tty, port->closing_wait);
1583 }
1584 /*
1585 * At this point we stop accepting input. To do this, we
1586 * disable the receive line status interrupts, and tell the
1587 * interrupt driver to stop checking the data ready bit in the
1588 * line status register.
1589 */
1590 dprintk (SX_DEBUG_OPEN, "Closed\n");
1591 port->IER &= ~IER_RXD;
1592 if (port->flags & ASYNC_INITIALIZED) {
1593 port->IER &= ~IER_TXRDY;
1594 port->IER |= IER_TXEMPTY;
1595 spin_lock_irqsave(&bp->lock, flags);
1596 sx_out(bp, CD186x_CAR, port_No(port));
1597 sx_out(bp, CD186x_IER, port->IER);
1598 spin_unlock_irqrestore(&bp->lock, flags);
1599 /*
1600 * Before we drop DTR, make sure the UART transmitter
1601 * has completely drained; this is especially
1602 * important if there is a transmit FIFO!
1603 */
1604 timeout = jiffies+HZ;
1605 while(port->IER & IER_TXEMPTY) {
1606 set_current_state (TASK_INTERRUPTIBLE);
1607 msleep_interruptible(jiffies_to_msecs(port->timeout));
1608 if (time_after(jiffies, timeout)) {
1609 printk (KERN_INFO "Timeout waiting for close\n");
1610 break;
1611 }
1612 }
1613
1614 }
1615
1616 if (--bp->count < 0) {
1617 printk(KERN_ERR "sx%d: sx_shutdown_port: bad board count: %d port: %d\n",
1618 board_No(bp), bp->count, tty->index);
1619 bp->count = 0;
1620 }
1621 if (--port->count < 0) {
1622 printk(KERN_ERR "sx%d: sx_close: bad port count for tty%d: %d\n",
1623 board_No(bp), port_No(port), port->count);
1624 port->count = 0;
1625 }
1626
1627 sx_shutdown_port(bp, port);
1628 if (tty->driver->flush_buffer)
1629 tty->driver->flush_buffer(tty);
1630 tty_ldisc_flush(tty);
1631 spin_lock_irqsave(&port->lock, flags);
1632 tty->closing = 0;
1633 port->event = 0;
1634 port->tty = NULL;
1635 spin_unlock_irqrestore(&port->lock, flags);
1636 if (port->blocked_open) {
1637 if (port->close_delay) {
1638 msleep_interruptible(jiffies_to_msecs(port->close_delay));
1639 }
1640 wake_up_interruptible(&port->open_wait);
1641 }
1642 port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
1643 wake_up_interruptible(&port->close_wait);
1644
1645 func_exit();
1646}
1647
1648
Jeff Garzikd61780c2005-10-30 15:01:51 -08001649static int sx_write(struct tty_struct * tty,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001650 const unsigned char *buf, int count)
1651{
1652 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1653 struct specialix_board *bp;
1654 int c, total = 0;
1655 unsigned long flags;
1656
1657 func_enter();
1658 if (sx_paranoia_check(port, tty->name, "sx_write")) {
1659 func_exit();
1660 return 0;
1661 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001662
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663 bp = port_Board(port);
1664
Jiri Slaby365e0222006-09-30 23:28:11 -07001665 if (!port->xmit_buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001666 func_exit();
1667 return 0;
1668 }
1669
1670 while (1) {
1671 spin_lock_irqsave(&port->lock, flags);
1672 c = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
1673 SERIAL_XMIT_SIZE - port->xmit_head));
1674 if (c <= 0) {
1675 spin_unlock_irqrestore(&port->lock, flags);
1676 break;
1677 }
1678 memcpy(port->xmit_buf + port->xmit_head, buf, c);
1679 port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
1680 port->xmit_cnt += c;
1681 spin_unlock_irqrestore(&port->lock, flags);
1682
1683 buf += c;
1684 count -= c;
1685 total += c;
1686 }
1687
1688 spin_lock_irqsave(&bp->lock, flags);
1689 if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
1690 !(port->IER & IER_TXRDY)) {
1691 port->IER |= IER_TXRDY;
1692 sx_out(bp, CD186x_CAR, port_No(port));
1693 sx_out(bp, CD186x_IER, port->IER);
1694 }
1695 spin_unlock_irqrestore(&bp->lock, flags);
1696 func_exit();
1697
1698 return total;
1699}
1700
1701
1702static void sx_put_char(struct tty_struct * tty, unsigned char ch)
1703{
1704 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1705 unsigned long flags;
1706 struct specialix_board * bp;
1707
1708 func_enter();
1709
1710 if (sx_paranoia_check(port, tty->name, "sx_put_char")) {
1711 func_exit();
1712 return;
1713 }
1714 dprintk (SX_DEBUG_TX, "check tty: %p %p\n", tty, port->xmit_buf);
Eric Sesterhenn326f28e92006-06-25 05:48:48 -07001715 if (!port->xmit_buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001716 func_exit();
1717 return;
1718 }
1719 bp = port_Board(port);
1720 spin_lock_irqsave(&port->lock, flags);
1721
1722 dprintk (SX_DEBUG_TX, "xmit_cnt: %d xmit_buf: %p\n", port->xmit_cnt, port->xmit_buf);
1723 if ((port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) || (!port->xmit_buf)) {
1724 spin_unlock_irqrestore(&port->lock, flags);
1725 dprintk (SX_DEBUG_TX, "Exit size\n");
1726 func_exit();
1727 return;
1728 }
1729 dprintk (SX_DEBUG_TX, "Handle xmit: %p %p\n", port, port->xmit_buf);
1730 port->xmit_buf[port->xmit_head++] = ch;
1731 port->xmit_head &= SERIAL_XMIT_SIZE - 1;
1732 port->xmit_cnt++;
1733 spin_unlock_irqrestore(&port->lock, flags);
1734
1735 func_exit();
1736}
1737
1738
1739static void sx_flush_chars(struct tty_struct * tty)
1740{
1741 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1742 unsigned long flags;
1743 struct specialix_board * bp = port_Board(port);
1744
1745 func_enter();
1746
1747 if (sx_paranoia_check(port, tty->name, "sx_flush_chars")) {
1748 func_exit();
1749 return;
1750 }
1751 if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
1752 !port->xmit_buf) {
1753 func_exit();
1754 return;
1755 }
1756 spin_lock_irqsave(&bp->lock, flags);
1757 port->IER |= IER_TXRDY;
1758 sx_out(port_Board(port), CD186x_CAR, port_No(port));
1759 sx_out(port_Board(port), CD186x_IER, port->IER);
1760 spin_unlock_irqrestore(&bp->lock, flags);
1761
1762 func_exit();
1763}
1764
1765
1766static int sx_write_room(struct tty_struct * tty)
1767{
1768 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1769 int ret;
1770
1771 func_enter();
1772
1773 if (sx_paranoia_check(port, tty->name, "sx_write_room")) {
1774 func_exit();
1775 return 0;
1776 }
1777
1778 ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;
1779 if (ret < 0)
1780 ret = 0;
1781
1782 func_exit();
1783 return ret;
1784}
1785
1786
1787static int sx_chars_in_buffer(struct tty_struct *tty)
1788{
1789 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1790
1791 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08001792
Linus Torvalds1da177e2005-04-16 15:20:36 -07001793 if (sx_paranoia_check(port, tty->name, "sx_chars_in_buffer")) {
1794 func_exit();
1795 return 0;
1796 }
1797 func_exit();
1798 return port->xmit_cnt;
1799}
1800
1801
1802static void sx_flush_buffer(struct tty_struct *tty)
1803{
1804 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1805 unsigned long flags;
1806 struct specialix_board * bp;
1807
1808 func_enter();
1809
1810 if (sx_paranoia_check(port, tty->name, "sx_flush_buffer")) {
1811 func_exit();
1812 return;
1813 }
1814
1815 bp = port_Board(port);
1816 spin_lock_irqsave(&port->lock, flags);
1817 port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
1818 spin_unlock_irqrestore(&port->lock, flags);
1819 tty_wakeup(tty);
1820
1821 func_exit();
1822}
1823
1824
1825static int sx_tiocmget(struct tty_struct *tty, struct file *file)
1826{
1827 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1828 struct specialix_board * bp;
1829 unsigned char status;
1830 unsigned int result;
1831 unsigned long flags;
1832
1833 func_enter();
1834
1835 if (sx_paranoia_check(port, tty->name, __FUNCTION__)) {
1836 func_exit();
1837 return -ENODEV;
1838 }
1839
1840 bp = port_Board(port);
1841 spin_lock_irqsave (&bp->lock, flags);
1842 sx_out(bp, CD186x_CAR, port_No(port));
1843 status = sx_in(bp, CD186x_MSVR);
1844 spin_unlock_irqrestore(&bp->lock, flags);
1845 dprintk (SX_DEBUG_INIT, "Got msvr[%d] = %02x, car = %d.\n",
1846 port_No(port), status, sx_in (bp, CD186x_CAR));
1847 dprintk (SX_DEBUG_INIT, "sx_port = %p, port = %p\n", sx_port, port);
1848 if (SX_CRTSCTS(port->tty)) {
Jeff Garzikd61780c2005-10-30 15:01:51 -08001849 result = /* (status & MSVR_RTS) ? */ TIOCM_DTR /* : 0) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001850 | ((status & MSVR_DTR) ? TIOCM_RTS : 0)
1851 | ((status & MSVR_CD) ? TIOCM_CAR : 0)
1852 |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */
1853 | ((status & MSVR_CTS) ? TIOCM_CTS : 0);
1854 } else {
Jeff Garzikd61780c2005-10-30 15:01:51 -08001855 result = /* (status & MSVR_RTS) ? */ TIOCM_RTS /* : 0) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001856 | ((status & MSVR_DTR) ? TIOCM_DTR : 0)
1857 | ((status & MSVR_CD) ? TIOCM_CAR : 0)
1858 |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */
1859 | ((status & MSVR_CTS) ? TIOCM_CTS : 0);
1860 }
1861
1862 func_exit();
1863
1864 return result;
1865}
1866
1867
1868static int sx_tiocmset(struct tty_struct *tty, struct file *file,
1869 unsigned int set, unsigned int clear)
1870{
1871 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1872 unsigned long flags;
1873 struct specialix_board *bp;
1874
1875 func_enter();
1876
1877 if (sx_paranoia_check(port, tty->name, __FUNCTION__)) {
1878 func_exit();
1879 return -ENODEV;
1880 }
1881
1882 bp = port_Board(port);
1883
1884 spin_lock_irqsave(&port->lock, flags);
1885 /* if (set & TIOCM_RTS)
1886 port->MSVR |= MSVR_RTS; */
1887 /* if (set & TIOCM_DTR)
1888 port->MSVR |= MSVR_DTR; */
1889
1890 if (SX_CRTSCTS(port->tty)) {
1891 if (set & TIOCM_RTS)
1892 port->MSVR |= MSVR_DTR;
1893 } else {
1894 if (set & TIOCM_DTR)
1895 port->MSVR |= MSVR_DTR;
1896 }
1897
1898 /* if (clear & TIOCM_RTS)
1899 port->MSVR &= ~MSVR_RTS; */
1900 /* if (clear & TIOCM_DTR)
1901 port->MSVR &= ~MSVR_DTR; */
1902 if (SX_CRTSCTS(port->tty)) {
1903 if (clear & TIOCM_RTS)
1904 port->MSVR &= ~MSVR_DTR;
1905 } else {
1906 if (clear & TIOCM_DTR)
1907 port->MSVR &= ~MSVR_DTR;
1908 }
1909 spin_lock_irqsave(&bp->lock, flags);
1910 sx_out(bp, CD186x_CAR, port_No(port));
1911 sx_out(bp, CD186x_MSVR, port->MSVR);
1912 spin_unlock_irqrestore(&bp->lock, flags);
1913 spin_unlock_irqrestore(&port->lock, flags);
1914 func_exit();
1915 return 0;
1916}
1917
1918
1919static inline void sx_send_break(struct specialix_port * port, unsigned long length)
1920{
1921 struct specialix_board *bp = port_Board(port);
1922 unsigned long flags;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001923
Linus Torvalds1da177e2005-04-16 15:20:36 -07001924 func_enter();
1925
1926 spin_lock_irqsave (&port->lock, flags);
1927 port->break_length = SPECIALIX_TPS / HZ * length;
1928 port->COR2 |= COR2_ETC;
1929 port->IER |= IER_TXRDY;
1930 spin_lock_irqsave(&bp->lock, flags);
1931 sx_out(bp, CD186x_CAR, port_No(port));
1932 sx_out(bp, CD186x_COR2, port->COR2);
1933 sx_out(bp, CD186x_IER, port->IER);
1934 spin_unlock_irqrestore(&bp->lock, flags);
1935 spin_unlock_irqrestore (&port->lock, flags);
1936 sx_wait_CCR(bp);
1937 spin_lock_irqsave(&bp->lock, flags);
1938 sx_out(bp, CD186x_CCR, CCR_CORCHG2);
1939 spin_unlock_irqrestore(&bp->lock, flags);
1940 sx_wait_CCR(bp);
1941
1942 func_exit();
1943}
1944
1945
1946static inline int sx_set_serial_info(struct specialix_port * port,
1947 struct serial_struct __user * newinfo)
1948{
1949 struct serial_struct tmp;
1950 struct specialix_board *bp = port_Board(port);
1951 int change_speed;
1952
1953 func_enter();
1954 /*
Jesper Juhle49332b2005-05-01 08:59:08 -07001955 if (!access_ok(VERIFY_READ, (void *) newinfo, sizeof(tmp))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001956 func_exit();
Jesper Juhle49332b2005-05-01 08:59:08 -07001957 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001958 }
1959 */
1960 if (copy_from_user(&tmp, newinfo, sizeof(tmp))) {
1961 func_enter();
1962 return -EFAULT;
1963 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001964
1965#if 0
Linus Torvalds1da177e2005-04-16 15:20:36 -07001966 if ((tmp.irq != bp->irq) ||
1967 (tmp.port != bp->base) ||
1968 (tmp.type != PORT_CIRRUS) ||
1969 (tmp.baud_base != (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC) ||
1970 (tmp.custom_divisor != 0) ||
1971 (tmp.xmit_fifo_size != CD186x_NFIFO) ||
1972 (tmp.flags & ~SPECIALIX_LEGAL_FLAGS)) {
1973 func_exit();
1974 return -EINVAL;
1975 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001976#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001977
1978 change_speed = ((port->flags & ASYNC_SPD_MASK) !=
1979 (tmp.flags & ASYNC_SPD_MASK));
1980 change_speed |= (tmp.custom_divisor != port->custom_divisor);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001981
Linus Torvalds1da177e2005-04-16 15:20:36 -07001982 if (!capable(CAP_SYS_ADMIN)) {
1983 if ((tmp.close_delay != port->close_delay) ||
1984 (tmp.closing_wait != port->closing_wait) ||
1985 ((tmp.flags & ~ASYNC_USR_MASK) !=
1986 (port->flags & ~ASYNC_USR_MASK))) {
1987 func_exit();
1988 return -EPERM;
1989 }
1990 port->flags = ((port->flags & ~ASYNC_USR_MASK) |
1991 (tmp.flags & ASYNC_USR_MASK));
1992 port->custom_divisor = tmp.custom_divisor;
1993 } else {
1994 port->flags = ((port->flags & ~ASYNC_FLAGS) |
1995 (tmp.flags & ASYNC_FLAGS));
1996 port->close_delay = tmp.close_delay;
1997 port->closing_wait = tmp.closing_wait;
1998 port->custom_divisor = tmp.custom_divisor;
1999 }
2000 if (change_speed) {
2001 sx_change_speed(bp, port);
2002 }
2003 func_exit();
2004 return 0;
2005}
2006
2007
2008static inline int sx_get_serial_info(struct specialix_port * port,
2009 struct serial_struct __user *retinfo)
2010{
2011 struct serial_struct tmp;
2012 struct specialix_board *bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002013
Linus Torvalds1da177e2005-04-16 15:20:36 -07002014 func_enter();
2015
2016 /*
Jesper Juhle49332b2005-05-01 08:59:08 -07002017 if (!access_ok(VERIFY_WRITE, (void *) retinfo, sizeof(tmp)))
2018 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002019 */
2020
2021 memset(&tmp, 0, sizeof(tmp));
2022 tmp.type = PORT_CIRRUS;
2023 tmp.line = port - sx_port;
2024 tmp.port = bp->base;
2025 tmp.irq = bp->irq;
2026 tmp.flags = port->flags;
2027 tmp.baud_base = (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC;
2028 tmp.close_delay = port->close_delay * HZ/100;
2029 tmp.closing_wait = port->closing_wait * HZ/100;
2030 tmp.custom_divisor = port->custom_divisor;
2031 tmp.xmit_fifo_size = CD186x_NFIFO;
2032 if (copy_to_user(retinfo, &tmp, sizeof(tmp))) {
2033 func_exit();
2034 return -EFAULT;
2035 }
2036
2037 func_exit();
2038 return 0;
2039}
2040
2041
Jeff Garzikd61780c2005-10-30 15:01:51 -08002042static int sx_ioctl(struct tty_struct * tty, struct file * filp,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002043 unsigned int cmd, unsigned long arg)
2044{
2045 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2046 int retval;
2047 void __user *argp = (void __user *)arg;
2048
2049 func_enter();
2050
2051 if (sx_paranoia_check(port, tty->name, "sx_ioctl")) {
2052 func_exit();
2053 return -ENODEV;
2054 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002055
Linus Torvalds1da177e2005-04-16 15:20:36 -07002056 switch (cmd) {
2057 case TCSBRK: /* SVID version: non-zero arg --> no break */
2058 retval = tty_check_change(tty);
2059 if (retval) {
2060 func_exit();
2061 return retval;
2062 }
2063 tty_wait_until_sent(tty, 0);
2064 if (!arg)
2065 sx_send_break(port, HZ/4); /* 1/4 second */
2066 return 0;
2067 case TCSBRKP: /* support for POSIX tcsendbreak() */
2068 retval = tty_check_change(tty);
2069 if (retval) {
2070 func_exit();
2071 return retval;
2072 }
2073 tty_wait_until_sent(tty, 0);
2074 sx_send_break(port, arg ? arg*(HZ/10) : HZ/4);
2075 func_exit();
2076 return 0;
2077 case TIOCGSOFTCAR:
2078 if (put_user(C_CLOCAL(tty)?1:0, (unsigned long __user *)argp)) {
2079 func_exit();
2080 return -EFAULT;
2081 }
2082 func_exit();
2083 return 0;
2084 case TIOCSSOFTCAR:
2085 if (get_user(arg, (unsigned long __user *) argp)) {
2086 func_exit();
2087 return -EFAULT;
2088 }
2089 tty->termios->c_cflag =
2090 ((tty->termios->c_cflag & ~CLOCAL) |
2091 (arg ? CLOCAL : 0));
2092 func_exit();
2093 return 0;
2094 case TIOCGSERIAL:
2095 func_exit();
2096 return sx_get_serial_info(port, argp);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002097 case TIOCSSERIAL:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002098 func_exit();
2099 return sx_set_serial_info(port, argp);
2100 default:
2101 func_exit();
2102 return -ENOIOCTLCMD;
2103 }
2104 func_exit();
2105 return 0;
2106}
2107
2108
2109static void sx_throttle(struct tty_struct * tty)
2110{
2111 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2112 struct specialix_board *bp;
2113 unsigned long flags;
2114
2115 func_enter();
2116
2117 if (sx_paranoia_check(port, tty->name, "sx_throttle")) {
2118 func_exit();
2119 return;
2120 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002121
Linus Torvalds1da177e2005-04-16 15:20:36 -07002122 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002123
Linus Torvalds1da177e2005-04-16 15:20:36 -07002124 /* Use DTR instead of RTS ! */
Jeff Garzikd61780c2005-10-30 15:01:51 -08002125 if (SX_CRTSCTS (tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002126 port->MSVR &= ~MSVR_DTR;
2127 else {
2128 /* Auch!!! I think the system shouldn't call this then. */
2129 /* Or maybe we're supposed (allowed?) to do our side of hw
Jeff Garzikd61780c2005-10-30 15:01:51 -08002130 handshake anyway, even when hardware handshake is off.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002131 When you see this in your logs, please report.... */
2132 printk (KERN_ERR "sx%d: Need to throttle, but can't (hardware hs is off)\n",
2133 port_No (port));
2134 }
2135 spin_lock_irqsave(&bp->lock, flags);
2136 sx_out(bp, CD186x_CAR, port_No(port));
2137 spin_unlock_irqrestore(&bp->lock, flags);
2138 if (I_IXOFF(tty)) {
2139 spin_unlock_irqrestore(&bp->lock, flags);
2140 sx_wait_CCR(bp);
2141 spin_lock_irqsave(&bp->lock, flags);
2142 sx_out(bp, CD186x_CCR, CCR_SSCH2);
2143 spin_unlock_irqrestore(&bp->lock, flags);
2144 sx_wait_CCR(bp);
2145 }
2146 spin_lock_irqsave(&bp->lock, flags);
2147 sx_out(bp, CD186x_MSVR, port->MSVR);
2148 spin_unlock_irqrestore(&bp->lock, flags);
2149
2150 func_exit();
2151}
2152
2153
2154static void sx_unthrottle(struct tty_struct * tty)
2155{
2156 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2157 struct specialix_board *bp;
2158 unsigned long flags;
2159
2160 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002161
Linus Torvalds1da177e2005-04-16 15:20:36 -07002162 if (sx_paranoia_check(port, tty->name, "sx_unthrottle")) {
2163 func_exit();
2164 return;
2165 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002166
Linus Torvalds1da177e2005-04-16 15:20:36 -07002167 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002168
Linus Torvalds1da177e2005-04-16 15:20:36 -07002169 spin_lock_irqsave(&port->lock, flags);
2170 /* XXXX Use DTR INSTEAD???? */
2171 if (SX_CRTSCTS(tty)) {
2172 port->MSVR |= MSVR_DTR;
2173 } /* Else clause: see remark in "sx_throttle"... */
2174 spin_lock_irqsave(&bp->lock, flags);
2175 sx_out(bp, CD186x_CAR, port_No(port));
2176 spin_unlock_irqrestore(&bp->lock, flags);
2177 if (I_IXOFF(tty)) {
2178 spin_unlock_irqrestore(&port->lock, flags);
2179 sx_wait_CCR(bp);
2180 spin_lock_irqsave(&bp->lock, flags);
2181 sx_out(bp, CD186x_CCR, CCR_SSCH1);
2182 spin_unlock_irqrestore(&bp->lock, flags);
2183 sx_wait_CCR(bp);
2184 spin_lock_irqsave(&port->lock, flags);
2185 }
2186 spin_lock_irqsave(&bp->lock, flags);
2187 sx_out(bp, CD186x_MSVR, port->MSVR);
2188 spin_unlock_irqrestore(&bp->lock, flags);
2189 spin_unlock_irqrestore(&port->lock, flags);
2190
2191 func_exit();
2192}
2193
2194
2195static void sx_stop(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_stop")) {
2204 func_exit();
2205 return;
2206 }
2207
2208 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 port->IER &= ~IER_TXRDY;
2212 spin_lock_irqsave(&bp->lock, flags);
2213 sx_out(bp, CD186x_CAR, port_No(port));
2214 sx_out(bp, CD186x_IER, port->IER);
2215 spin_unlock_irqrestore(&bp->lock, flags);
2216 spin_unlock_irqrestore(&port->lock, flags);
2217
2218 func_exit();
2219}
2220
2221
2222static void sx_start(struct tty_struct * tty)
2223{
2224 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2225 struct specialix_board *bp;
2226 unsigned long flags;
2227
2228 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002229
Linus Torvalds1da177e2005-04-16 15:20:36 -07002230 if (sx_paranoia_check(port, tty->name, "sx_start")) {
2231 func_exit();
2232 return;
2233 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002234
Linus Torvalds1da177e2005-04-16 15:20:36 -07002235 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002236
Linus Torvalds1da177e2005-04-16 15:20:36 -07002237 spin_lock_irqsave(&port->lock, flags);
2238 if (port->xmit_cnt && port->xmit_buf && !(port->IER & IER_TXRDY)) {
2239 port->IER |= IER_TXRDY;
2240 spin_lock_irqsave(&bp->lock, flags);
2241 sx_out(bp, CD186x_CAR, port_No(port));
2242 sx_out(bp, CD186x_IER, port->IER);
2243 spin_unlock_irqrestore(&bp->lock, flags);
2244 }
2245 spin_unlock_irqrestore(&port->lock, flags);
2246
2247 func_exit();
2248}
2249
2250
2251/*
2252 * This routine is called from the work-queue when the interrupt
2253 * routine has signalled that a hangup has occurred. The path of
2254 * hangup processing is:
2255 *
2256 * serial interrupt routine -> (workqueue) ->
2257 * do_sx_hangup() -> tty->hangup() -> sx_hangup()
Jeff Garzikd61780c2005-10-30 15:01:51 -08002258 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07002259 */
David Howellsc4028952006-11-22 14:57:56 +00002260static void do_sx_hangup(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002261{
David Howellsc4028952006-11-22 14:57:56 +00002262 struct specialix_port *port =
2263 container_of(work, struct specialix_port, tqueue_hangup);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002264 struct tty_struct *tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002265
Linus Torvalds1da177e2005-04-16 15:20:36 -07002266 func_enter();
2267
2268 tty = port->tty;
2269 if (tty)
2270 tty_hangup(tty); /* FIXME: module removal race here */
2271
2272 func_exit();
2273}
2274
2275
2276static void sx_hangup(struct tty_struct * tty)
2277{
2278 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2279 struct specialix_board *bp;
2280 unsigned long flags;
2281
2282 func_enter();
2283
2284 if (sx_paranoia_check(port, tty->name, "sx_hangup")) {
2285 func_exit();
2286 return;
2287 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002288
Linus Torvalds1da177e2005-04-16 15:20:36 -07002289 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002290
Linus Torvalds1da177e2005-04-16 15:20:36 -07002291 sx_shutdown_port(bp, port);
2292 spin_lock_irqsave(&port->lock, flags);
2293 port->event = 0;
2294 bp->count -= port->count;
2295 if (bp->count < 0) {
2296 printk(KERN_ERR "sx%d: sx_hangup: bad board count: %d port: %d\n",
2297 board_No(bp), bp->count, tty->index);
2298 bp->count = 0;
2299 }
2300 port->count = 0;
2301 port->flags &= ~ASYNC_NORMAL_ACTIVE;
2302 port->tty = NULL;
2303 spin_unlock_irqrestore(&port->lock, flags);
2304 wake_up_interruptible(&port->open_wait);
2305
2306 func_exit();
2307}
2308
2309
Alan Cox606d0992006-12-08 02:38:45 -08002310static void sx_set_termios(struct tty_struct * tty, struct ktermios * old_termios)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002311{
2312 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2313 unsigned long flags;
2314 struct specialix_board * bp;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002315
Linus Torvalds1da177e2005-04-16 15:20:36 -07002316 if (sx_paranoia_check(port, tty->name, "sx_set_termios"))
2317 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002318
Linus Torvalds1da177e2005-04-16 15:20:36 -07002319 if (tty->termios->c_cflag == old_termios->c_cflag &&
2320 tty->termios->c_iflag == old_termios->c_iflag)
2321 return;
2322
2323 bp = port_Board(port);
2324 spin_lock_irqsave(&port->lock, flags);
2325 sx_change_speed(port_Board(port), port);
2326 spin_unlock_irqrestore(&port->lock, flags);
2327
2328 if ((old_termios->c_cflag & CRTSCTS) &&
2329 !(tty->termios->c_cflag & CRTSCTS)) {
2330 tty->hw_stopped = 0;
2331 sx_start(tty);
2332 }
2333}
2334
2335
David Howellsc4028952006-11-22 14:57:56 +00002336static void do_softint(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002337{
David Howellsc4028952006-11-22 14:57:56 +00002338 struct specialix_port *port =
2339 container_of(work, struct specialix_port, tqueue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002340 struct tty_struct *tty;
2341
2342 func_enter();
2343
2344 if(!(tty = port->tty)) {
2345 func_exit();
2346 return;
2347 }
2348
Jiri Slabyb963a842007-02-10 01:44:55 -08002349 if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002350 tty_wakeup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002351
2352 func_exit();
2353}
2354
Jeff Dikeb68e31d2006-10-02 02:17:18 -07002355static const struct tty_operations sx_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002356 .open = sx_open,
2357 .close = sx_close,
2358 .write = sx_write,
2359 .put_char = sx_put_char,
2360 .flush_chars = sx_flush_chars,
2361 .write_room = sx_write_room,
2362 .chars_in_buffer = sx_chars_in_buffer,
2363 .flush_buffer = sx_flush_buffer,
2364 .ioctl = sx_ioctl,
2365 .throttle = sx_throttle,
2366 .unthrottle = sx_unthrottle,
2367 .set_termios = sx_set_termios,
2368 .stop = sx_stop,
2369 .start = sx_start,
2370 .hangup = sx_hangup,
2371 .tiocmget = sx_tiocmget,
2372 .tiocmset = sx_tiocmset,
2373};
2374
2375static int sx_init_drivers(void)
2376{
2377 int error;
2378 int i;
2379
2380 func_enter();
2381
2382 specialix_driver = alloc_tty_driver(SX_NBOARD * SX_NPORT);
2383 if (!specialix_driver) {
2384 printk(KERN_ERR "sx: Couldn't allocate tty_driver.\n");
2385 func_exit();
2386 return 1;
2387 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002388
Linus Torvalds1da177e2005-04-16 15:20:36 -07002389 specialix_driver->owner = THIS_MODULE;
2390 specialix_driver->name = "ttyW";
2391 specialix_driver->major = SPECIALIX_NORMAL_MAJOR;
2392 specialix_driver->type = TTY_DRIVER_TYPE_SERIAL;
2393 specialix_driver->subtype = SERIAL_TYPE_NORMAL;
2394 specialix_driver->init_termios = tty_std_termios;
2395 specialix_driver->init_termios.c_cflag =
2396 B9600 | CS8 | CREAD | HUPCL | CLOCAL;
Alan Cox606d0992006-12-08 02:38:45 -08002397 specialix_driver->init_termios.c_ispeed = 9600;
2398 specialix_driver->init_termios.c_ospeed = 9600;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002399 specialix_driver->flags = TTY_DRIVER_REAL_RAW;
2400 tty_set_operations(specialix_driver, &sx_ops);
2401
2402 if ((error = tty_register_driver(specialix_driver))) {
2403 put_tty_driver(specialix_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002404 printk(KERN_ERR "sx: Couldn't register specialix IO8+ driver, error = %d\n",
2405 error);
2406 func_exit();
2407 return 1;
2408 }
2409 memset(sx_port, 0, sizeof(sx_port));
2410 for (i = 0; i < SX_NPORT * SX_NBOARD; i++) {
2411 sx_port[i].magic = SPECIALIX_MAGIC;
David Howellsc4028952006-11-22 14:57:56 +00002412 INIT_WORK(&sx_port[i].tqueue, do_softint);
2413 INIT_WORK(&sx_port[i].tqueue_hangup, do_sx_hangup);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002414 sx_port[i].close_delay = 50 * HZ/100;
2415 sx_port[i].closing_wait = 3000 * HZ/100;
2416 init_waitqueue_head(&sx_port[i].open_wait);
2417 init_waitqueue_head(&sx_port[i].close_wait);
2418 spin_lock_init(&sx_port[i].lock);
2419 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002420
Linus Torvalds1da177e2005-04-16 15:20:36 -07002421 func_exit();
2422 return 0;
2423}
2424
2425static void sx_release_drivers(void)
2426{
2427 func_enter();
2428
Linus Torvalds1da177e2005-04-16 15:20:36 -07002429 tty_unregister_driver(specialix_driver);
2430 put_tty_driver(specialix_driver);
2431 func_exit();
2432}
2433
Jeff Garzikd61780c2005-10-30 15:01:51 -08002434/*
2435 * This routine must be called by kernel at boot time
Linus Torvalds1da177e2005-04-16 15:20:36 -07002436 */
2437static int __init specialix_init(void)
2438{
2439 int i;
2440 int found = 0;
2441
2442 func_enter();
2443
2444 printk(KERN_INFO "sx: Specialix IO8+ driver v" VERSION ", (c) R.E.Wolff 1997/1998.\n");
2445 printk(KERN_INFO "sx: derived from work (c) D.Gorodchanin 1994-1996.\n");
2446#ifdef CONFIG_SPECIALIX_RTSCTS
2447 printk (KERN_INFO "sx: DTR/RTS pin is always RTS.\n");
2448#else
2449 printk (KERN_INFO "sx: DTR/RTS pin is RTS when CRTSCTS is on.\n");
2450#endif
Jeff Garzikd61780c2005-10-30 15:01:51 -08002451
Linus Torvalds1da177e2005-04-16 15:20:36 -07002452 for (i = 0; i < SX_NBOARD; i++)
Ingo Molnar34af9462006-06-27 02:53:55 -07002453 spin_lock_init(&sx_board[i].lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002454
2455 if (sx_init_drivers()) {
2456 func_exit();
2457 return -EIO;
2458 }
2459
Jeff Garzikd61780c2005-10-30 15:01:51 -08002460 for (i = 0; i < SX_NBOARD; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002461 if (sx_board[i].base && !sx_probe(&sx_board[i]))
2462 found++;
2463
2464#ifdef CONFIG_PCI
2465 {
2466 struct pci_dev *pdev = NULL;
2467
2468 i=0;
2469 while (i < SX_NBOARD) {
2470 if (sx_board[i].flags & SX_BOARD_PRESENT) {
2471 i++;
2472 continue;
2473 }
Alan Cox606d0992006-12-08 02:38:45 -08002474 pdev = pci_get_device (PCI_VENDOR_ID_SPECIALIX,
Jeff Garzikd61780c2005-10-30 15:01:51 -08002475 PCI_DEVICE_ID_SPECIALIX_IO8,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002476 pdev);
2477 if (!pdev) break;
2478
2479 if (pci_enable_device(pdev))
2480 continue;
2481
2482 sx_board[i].irq = pdev->irq;
2483
2484 sx_board[i].base = pci_resource_start (pdev, 2);
2485
2486 sx_board[i].flags |= SX_BOARD_IS_PCI;
2487 if (!sx_probe(&sx_board[i]))
2488 found ++;
2489 }
Alan Cox606d0992006-12-08 02:38:45 -08002490 /* May exit pci_get sequence early with lots of boards */
2491 if (pdev != NULL)
2492 pci_dev_put(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002493 }
2494#endif
2495
2496 if (!found) {
2497 sx_release_drivers();
2498 printk(KERN_INFO "sx: No specialix IO8+ boards detected.\n");
2499 func_exit();
2500 return -EIO;
2501 }
2502
2503 func_exit();
2504 return 0;
2505}
2506
2507static int iobase[SX_NBOARD] = {0,};
2508
2509static int irq [SX_NBOARD] = {0,};
2510
2511module_param_array(iobase, int, NULL, 0);
2512module_param_array(irq, int, NULL, 0);
2513module_param(sx_debug, int, 0);
2514module_param(sx_rxfifo, int, 0);
2515#ifdef SPECIALIX_TIMER
2516module_param(sx_poll, int, 0);
2517#endif
2518
2519/*
2520 * You can setup up to 4 boards.
2521 * by specifying "iobase=0xXXX,0xXXX ..." as insmod parameter.
Jeff Garzikd61780c2005-10-30 15:01:51 -08002522 * You should specify the IRQs too in that case "irq=....,...".
2523 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07002524 * More than 4 boards in one computer is not possible, as the card can
Jeff Garzikd61780c2005-10-30 15:01:51 -08002525 * only use 4 different interrupts.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002526 *
2527 */
2528static int __init specialix_init_module(void)
2529{
2530 int i;
2531
2532 func_enter();
2533
Linus Torvalds1da177e2005-04-16 15:20:36 -07002534 if (iobase[0] || iobase[1] || iobase[2] || iobase[3]) {
2535 for(i = 0; i < SX_NBOARD; i++) {
2536 sx_board[i].base = iobase[i];
2537 sx_board[i].irq = irq[i];
2538 sx_board[i].count= 0;
2539 }
2540 }
2541
2542 func_exit();
2543
2544 return specialix_init();
2545}
Jeff Garzikd61780c2005-10-30 15:01:51 -08002546
Linus Torvalds1da177e2005-04-16 15:20:36 -07002547static void __exit specialix_exit_module(void)
2548{
2549 int i;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002550
Linus Torvalds1da177e2005-04-16 15:20:36 -07002551 func_enter();
2552
2553 sx_release_drivers();
2554 for (i = 0; i < SX_NBOARD; i++)
Jeff Garzikd61780c2005-10-30 15:01:51 -08002555 if (sx_board[i].flags & SX_BOARD_PRESENT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002556 sx_release_io_range(&sx_board[i]);
2557#ifdef SPECIALIX_TIMER
Jiri Slaby40565f12007-02-12 00:52:31 -08002558 del_timer_sync(&missed_irq_timer);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002559#endif
2560
2561 func_exit();
2562}
2563
Chuck Short76910302006-07-10 04:43:59 -07002564static struct pci_device_id specialx_pci_tbl[] __devinitdata = {
2565 { PCI_DEVICE(PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_SPECIALIX_IO8) },
2566 { }
2567};
2568MODULE_DEVICE_TABLE(pci, specialx_pci_tbl);
2569
Linus Torvalds1da177e2005-04-16 15:20:36 -07002570module_init(specialix_init_module);
2571module_exit(specialix_exit_module);
2572
2573MODULE_LICENSE("GPL");