blob: cb2859249d49a9171899db66f9661f36fdcfdb90 [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;
185static unsigned char * tmp_buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186
187static unsigned long baud_table[] = {
188 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
Jeff Garzikd61780c2005-10-30 15:01:51 -0800189 9600, 19200, 38400, 57600, 115200, 0,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190};
191
192static struct specialix_board sx_board[SX_NBOARD] = {
193 { 0, SX_IOBASE1, 9, },
194 { 0, SX_IOBASE2, 11, },
195 { 0, SX_IOBASE3, 12, },
196 { 0, SX_IOBASE4, 15, },
197};
198
199static struct specialix_port sx_port[SX_NBOARD * SX_NPORT];
200
201
202#ifdef SPECIALIX_TIMER
203static struct timer_list missed_irq_timer;
204static irqreturn_t sx_interrupt(int irq, void * dev_id, struct pt_regs * regs);
205#endif
206
207
208
209static inline int sx_paranoia_check(struct specialix_port const * port,
210 char *name, const char *routine)
211{
212#ifdef SPECIALIX_PARANOIA_CHECK
213 static const char *badmagic =
214 KERN_ERR "sx: Warning: bad specialix port magic number for device %s in %s\n";
215 static const char *badinfo =
216 KERN_ERR "sx: Warning: null specialix port for device %s in %s\n";
Jeff Garzikd61780c2005-10-30 15:01:51 -0800217
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218 if (!port) {
219 printk(badinfo, name, routine);
220 return 1;
221 }
222 if (port->magic != SPECIALIX_MAGIC) {
223 printk(badmagic, name, routine);
224 return 1;
225 }
226#endif
227 return 0;
228}
229
230
231/*
Jeff Garzikd61780c2005-10-30 15:01:51 -0800232 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233 * Service functions for specialix IO8+ driver.
Jeff Garzikd61780c2005-10-30 15:01:51 -0800234 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235 */
236
237/* Get board number from pointer */
238static inline int board_No (struct specialix_board * bp)
239{
240 return bp - sx_board;
241}
242
243
244/* Get port number from pointer */
245static inline int port_No (struct specialix_port const * port)
246{
Jeff Garzikd61780c2005-10-30 15:01:51 -0800247 return SX_PORT(port - sx_port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248}
249
250
251/* Get pointer to board from pointer to port */
252static inline struct specialix_board * port_Board(struct specialix_port const * port)
253{
254 return &sx_board[SX_BOARD(port - sx_port)];
255}
256
257
258/* Input Byte from CL CD186x register */
259static inline unsigned char sx_in(struct specialix_board * bp, unsigned short reg)
260{
261 bp->reg = reg | 0x80;
262 outb (reg | 0x80, bp->base + SX_ADDR_REG);
263 return inb (bp->base + SX_DATA_REG);
264}
265
266
267/* Output Byte to CL CD186x register */
268static inline void sx_out(struct specialix_board * bp, unsigned short reg,
269 unsigned char val)
270{
271 bp->reg = reg | 0x80;
272 outb (reg | 0x80, bp->base + SX_ADDR_REG);
273 outb (val, bp->base + SX_DATA_REG);
274}
275
276
277/* Input Byte from CL CD186x register */
278static inline unsigned char sx_in_off(struct specialix_board * bp, unsigned short reg)
279{
280 bp->reg = reg;
281 outb (reg, bp->base + SX_ADDR_REG);
282 return inb (bp->base + SX_DATA_REG);
283}
284
285
286/* Output Byte to CL CD186x register */
287static inline void sx_out_off(struct specialix_board * bp, unsigned short reg,
288 unsigned char val)
289{
290 bp->reg = reg;
291 outb (reg, bp->base + SX_ADDR_REG);
292 outb (val, bp->base + SX_DATA_REG);
293}
294
295
296/* Wait for Channel Command Register ready */
297static inline void sx_wait_CCR(struct specialix_board * bp)
298{
299 unsigned long delay, flags;
300 unsigned char ccr;
301
302 for (delay = SX_CCR_TIMEOUT; delay; delay--) {
303 spin_lock_irqsave(&bp->lock, flags);
304 ccr = sx_in(bp, CD186x_CCR);
305 spin_unlock_irqrestore(&bp->lock, flags);
306 if (!ccr)
307 return;
308 udelay (1);
309 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800310
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp));
312}
313
314
315/* Wait for Channel Command Register ready */
316static inline void sx_wait_CCR_off(struct specialix_board * bp)
317{
318 unsigned long delay;
319 unsigned char crr;
320 unsigned long flags;
321
322 for (delay = SX_CCR_TIMEOUT; delay; delay--) {
323 spin_lock_irqsave(&bp->lock, flags);
324 crr = sx_in_off(bp, CD186x_CCR);
325 spin_unlock_irqrestore(&bp->lock, flags);
326 if (!crr)
327 return;
328 udelay (1);
329 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800330
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp));
332}
333
334
335/*
336 * specialix IO8+ IO range functions.
337 */
338
Jeff Garzikd61780c2005-10-30 15:01:51 -0800339static inline int sx_request_io_range(struct specialix_board * bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340{
Jeff Garzikd61780c2005-10-30 15:01:51 -0800341 return request_region(bp->base,
342 bp->flags & SX_BOARD_IS_PCI ? SX_PCI_IO_SPACE : SX_IO_SPACE,
343 "specialix IO8+") == NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344}
345
346
347static inline void sx_release_io_range(struct specialix_board * bp)
348{
Jeff Garzikd61780c2005-10-30 15:01:51 -0800349 release_region(bp->base,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350 bp->flags&SX_BOARD_IS_PCI?SX_PCI_IO_SPACE:SX_IO_SPACE);
351}
352
Jeff Garzikd61780c2005-10-30 15:01:51 -0800353
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354/* Must be called with enabled interrupts */
Jeff Garzikd61780c2005-10-30 15:01:51 -0800355/* Ugly. Very ugly. Don't use this for anything else than initialization
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 code */
357static inline void sx_long_delay(unsigned long delay)
358{
359 unsigned long i;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800360
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361 for (i = jiffies + delay; time_after(i, jiffies); ) ;
362}
363
364
365
366/* Set the IRQ using the RTS lines that run to the PAL on the board.... */
367static int sx_set_irq ( struct specialix_board *bp)
368{
369 int virq;
370 int i;
371 unsigned long flags;
372
Jeff Garzikd61780c2005-10-30 15:01:51 -0800373 if (bp->flags & SX_BOARD_IS_PCI)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374 return 1;
375 switch (bp->irq) {
376 /* In the same order as in the docs... */
377 case 15: virq = 0;break;
378 case 12: virq = 1;break;
379 case 11: virq = 2;break;
380 case 9: virq = 3;break;
381 default: printk (KERN_ERR "Speclialix: cannot set irq to %d.\n", bp->irq);
382 return 0;
383 }
384 spin_lock_irqsave(&bp->lock, flags);
385 for (i=0;i<2;i++) {
386 sx_out(bp, CD186x_CAR, i);
387 sx_out(bp, CD186x_MSVRTS, ((virq >> i) & 0x1)? MSVR_RTS:0);
388 }
389 spin_unlock_irqrestore(&bp->lock, flags);
390 return 1;
391}
392
393
394/* Reset and setup CD186x chip */
395static int sx_init_CD186x(struct specialix_board * bp)
396{
397 unsigned long flags;
398 int scaler;
399 int rv = 1;
400
401 func_enter();
402 sx_wait_CCR_off(bp); /* Wait for CCR ready */
403 spin_lock_irqsave(&bp->lock, flags);
404 sx_out_off(bp, CD186x_CCR, CCR_HARDRESET); /* Reset CD186x chip */
405 spin_unlock_irqrestore(&bp->lock, flags);
406 sx_long_delay(HZ/20); /* Delay 0.05 sec */
407 spin_lock_irqsave(&bp->lock, flags);
408 sx_out_off(bp, CD186x_GIVR, SX_ID); /* Set ID for this chip */
409 sx_out_off(bp, CD186x_GICR, 0); /* Clear all bits */
410 sx_out_off(bp, CD186x_PILR1, SX_ACK_MINT); /* Prio for modem intr */
411 sx_out_off(bp, CD186x_PILR2, SX_ACK_TINT); /* Prio for transmitter intr */
412 sx_out_off(bp, CD186x_PILR3, SX_ACK_RINT); /* Prio for receiver intr */
413 /* Set RegAckEn */
414 sx_out_off(bp, CD186x_SRCR, sx_in (bp, CD186x_SRCR) | SRCR_REGACKEN);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800415
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 /* Setting up prescaler. We need 4 ticks per 1 ms */
417 scaler = SX_OSCFREQ/SPECIALIX_TPS;
418
419 sx_out_off(bp, CD186x_PPRH, scaler >> 8);
420 sx_out_off(bp, CD186x_PPRL, scaler & 0xff);
421 spin_unlock_irqrestore(&bp->lock, flags);
422
423 if (!sx_set_irq (bp)) {
424 /* Figure out how to pass this along... */
425 printk (KERN_ERR "Cannot set irq to %d.\n", bp->irq);
426 rv = 0;
427 }
428
429 func_exit();
430 return rv;
431}
432
433
434static int read_cross_byte (struct specialix_board *bp, int reg, int bit)
435{
436 int i;
437 int t;
438 unsigned long flags;
439
440 spin_lock_irqsave(&bp->lock, flags);
441 for (i=0, t=0;i<8;i++) {
442 sx_out_off (bp, CD186x_CAR, i);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800443 if (sx_in_off (bp, reg) & bit)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444 t |= 1 << i;
445 }
446 spin_unlock_irqrestore(&bp->lock, flags);
447
448 return t;
449}
450
451
452#ifdef SPECIALIX_TIMER
453void missed_irq (unsigned long data)
454{
455 unsigned char irq;
456 unsigned long flags;
457 struct specialix_board *bp = (struct specialix_board *)data;
458
459 spin_lock_irqsave(&bp->lock, flags);
460 irq = sx_in ((struct specialix_board *)data, CD186x_SRSR) &
461 (SRSR_RREQint |
462 SRSR_TREQint |
463 SRSR_MREQint);
464 spin_unlock_irqrestore(&bp->lock, flags);
465 if (irq) {
466 printk (KERN_INFO "Missed interrupt... Calling int from timer. \n");
Jeff Garzikd61780c2005-10-30 15:01:51 -0800467 sx_interrupt (((struct specialix_board *)data)->irq,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468 (void*)data, NULL);
469 }
470 missed_irq_timer.expires = jiffies + sx_poll;
471 add_timer (&missed_irq_timer);
472}
473#endif
474
475
476
477/* Main probing routine, also sets irq. */
478static int sx_probe(struct specialix_board *bp)
479{
480 unsigned char val1, val2;
481#if 0
482 int irqs = 0;
483 int retries;
484#endif
485 int rev;
486 int chip;
487
488 func_enter();
489
Jeff Garzikd61780c2005-10-30 15:01:51 -0800490 if (sx_request_io_range(bp)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491 func_exit();
492 return 1;
493 }
494
495 /* Are the I/O ports here ? */
496 sx_out_off(bp, CD186x_PPRL, 0x5a);
497 short_pause ();
498 val1 = sx_in_off(bp, CD186x_PPRL);
499
500 sx_out_off(bp, CD186x_PPRL, 0xa5);
501 short_pause ();
502 val2 = sx_in_off(bp, CD186x_PPRL);
503
Jeff Garzikd61780c2005-10-30 15:01:51 -0800504
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505 if ((val1 != 0x5a) || (val2 != 0xa5)) {
506 printk(KERN_INFO "sx%d: specialix IO8+ Board at 0x%03x not found.\n",
507 board_No(bp), bp->base);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800508 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509 func_exit();
510 return 1;
511 }
512
Jeff Garzikd61780c2005-10-30 15:01:51 -0800513 /* Check the DSR lines that Specialix uses as board
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514 identification */
515 val1 = read_cross_byte (bp, CD186x_MSVR, MSVR_DSR);
516 val2 = read_cross_byte (bp, CD186x_MSVR, MSVR_RTS);
517 dprintk (SX_DEBUG_INIT, "sx%d: DSR lines are: %02x, rts lines are: %02x\n",
518 board_No(bp), val1, val2);
519
520 /* They managed to switch the bit order between the docs and
521 the IO8+ card. The new PCI card now conforms to old docs.
522 They changed the PCI docs to reflect the situation on the
523 old card. */
524 val2 = (bp->flags & SX_BOARD_IS_PCI)?0x4d : 0xb2;
525 if (val1 != val2) {
526 printk(KERN_INFO "sx%d: specialix IO8+ ID %02x at 0x%03x not found (%02x).\n",
527 board_No(bp), val2, bp->base, val1);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800528 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529 func_exit();
530 return 1;
531 }
532
533
534#if 0
535 /* It's time to find IRQ for this board */
536 for (retries = 0; retries < 5 && irqs <= 0; retries++) {
537 irqs = probe_irq_on();
538 sx_init_CD186x(bp); /* Reset CD186x chip */
539 sx_out(bp, CD186x_CAR, 2); /* Select port 2 */
540 sx_wait_CCR(bp);
541 sx_out(bp, CD186x_CCR, CCR_TXEN); /* Enable transmitter */
542 sx_out(bp, CD186x_IER, IER_TXRDY); /* Enable tx empty intr */
Jeff Garzikd61780c2005-10-30 15:01:51 -0800543 sx_long_delay(HZ/20);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 irqs = probe_irq_off(irqs);
545
546 dprintk (SX_DEBUG_INIT, "SRSR = %02x, ", sx_in(bp, CD186x_SRSR));
547 dprintk (SX_DEBUG_INIT, "TRAR = %02x, ", sx_in(bp, CD186x_TRAR));
548 dprintk (SX_DEBUG_INIT, "GIVR = %02x, ", sx_in(bp, CD186x_GIVR));
549 dprintk (SX_DEBUG_INIT, "GICR = %02x, ", sx_in(bp, CD186x_GICR));
550 dprintk (SX_DEBUG_INIT, "\n");
551
552 /* Reset CD186x again */
553 if (!sx_init_CD186x(bp)) {
554 /* Hmmm. This is dead code anyway. */
555 }
556
557 dprintk (SX_DEBUG_INIT "val1 = %02x, val2 = %02x, val3 = %02x.\n",
Jeff Garzikd61780c2005-10-30 15:01:51 -0800558 val1, val2, val3);
559
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800561
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562#if 0
563 if (irqs <= 0) {
564 printk(KERN_ERR "sx%d: Can't find IRQ for specialix IO8+ board at 0x%03x.\n",
565 board_No(bp), bp->base);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800566 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567 func_exit();
568 return 1;
569 }
570#endif
571 printk (KERN_INFO "Started with irq=%d, but now have irq=%d.\n", bp->irq, irqs);
572 if (irqs > 0)
573 bp->irq = irqs;
574#endif
575 /* Reset CD186x again */
576 if (!sx_init_CD186x(bp)) {
Jeff Garzikd61780c2005-10-30 15:01:51 -0800577 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 func_exit();
Jeff Garzikd61780c2005-10-30 15:01:51 -0800579 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580 }
581
582 sx_request_io_range(bp);
583 bp->flags |= SX_BOARD_PRESENT;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800584
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585 /* Chip revcode pkgtype
586 GFRCR SRCR bit 7
587 CD180 rev B 0x81 0
588 CD180 rev C 0x82 0
589 CD1864 rev A 0x82 1
Jeff Garzikd61780c2005-10-30 15:01:51 -0800590 CD1865 rev A 0x83 1 -- Do not use!!! Does not work.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591 CD1865 rev B 0x84 1
592 -- Thanks to Gwen Wang, Cirrus Logic.
593 */
594
595 switch (sx_in_off(bp, CD186x_GFRCR)) {
596 case 0x82:chip = 1864;rev='A';break;
597 case 0x83:chip = 1865;rev='A';break;
598 case 0x84:chip = 1865;rev='B';break;
599 case 0x85:chip = 1865;rev='C';break; /* Does not exist at this time */
600 default:chip=-1;rev='x';
601 }
602
603 dprintk (SX_DEBUG_INIT, " GFCR = 0x%02x\n", sx_in_off(bp, CD186x_GFRCR) );
604
605#ifdef SPECIALIX_TIMER
606 init_timer (&missed_irq_timer);
607 missed_irq_timer.function = missed_irq;
608 missed_irq_timer.data = (unsigned long) bp;
609 missed_irq_timer.expires = jiffies + sx_poll;
610 add_timer (&missed_irq_timer);
611#endif
612
613 printk(KERN_INFO"sx%d: specialix IO8+ board detected at 0x%03x, IRQ %d, CD%d Rev. %c.\n",
614 board_No(bp),
615 bp->base, bp->irq,
616 chip, rev);
617
618 func_exit();
619 return 0;
620}
621
Jeff Garzikd61780c2005-10-30 15:01:51 -0800622/*
623 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624 * Interrupt processing routines.
625 * */
626
627static inline void sx_mark_event(struct specialix_port * port, int event)
628{
629 func_enter();
630
631 set_bit(event, &port->event);
632 schedule_work(&port->tqueue);
633
634 func_exit();
635}
636
637
638static inline struct specialix_port * sx_get_port(struct specialix_board * bp,
639 unsigned char const * what)
640{
641 unsigned char channel;
642 struct specialix_port * port = NULL;
643
644 channel = sx_in(bp, CD186x_GICR) >> GICR_CHAN_OFF;
645 dprintk (SX_DEBUG_CHAN, "channel: %d\n", channel);
646 if (channel < CD186x_NCH) {
647 port = &sx_port[board_No(bp) * SX_NPORT + channel];
648 dprintk (SX_DEBUG_CHAN, "port: %d %p flags: 0x%x\n",board_No(bp) * SX_NPORT + channel, port, port->flags & ASYNC_INITIALIZED);
649
650 if (port->flags & ASYNC_INITIALIZED) {
651 dprintk (SX_DEBUG_CHAN, "port: %d %p\n", channel, port);
652 func_exit();
653 return port;
654 }
655 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800656 printk(KERN_INFO "sx%d: %s interrupt from invalid port %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657 board_No(bp), what, channel);
658 return NULL;
659}
660
661
662static inline void sx_receive_exc(struct specialix_board * bp)
663{
664 struct specialix_port *port;
665 struct tty_struct *tty;
666 unsigned char status;
Alan Cox33f0f882006-01-09 20:54:13 -0800667 unsigned char ch, flag;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668
669 func_enter();
670
671 port = sx_get_port(bp, "Receive");
672 if (!port) {
673 dprintk (SX_DEBUG_RX, "Hmm, couldn't find port.\n");
674 func_exit();
675 return;
676 }
677 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800678
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 status = sx_in(bp, CD186x_RCSR);
680
681 dprintk (SX_DEBUG_RX, "status: 0x%x\n", status);
682 if (status & RCSR_OE) {
683 port->overrun++;
684 dprintk(SX_DEBUG_FIFO, "sx%d: port %d: Overrun. Total %ld overruns.\n",
685 board_No(bp), port_No(port), port->overrun);
686 }
687 status &= port->mark_mask;
688
689 /* This flip buffer check needs to be below the reading of the
690 status register to reset the chip's IRQ.... */
Alan Cox33f0f882006-01-09 20:54:13 -0800691 if (tty_buffer_request_room(tty, 1) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692 dprintk(SX_DEBUG_FIFO, "sx%d: port %d: Working around flip buffer overflow.\n",
693 board_No(bp), port_No(port));
694 func_exit();
695 return;
696 }
697
698 ch = sx_in(bp, CD186x_RDR);
699 if (!status) {
700 func_exit();
701 return;
702 }
703 if (status & RCSR_TOUT) {
Jeff Garzikd61780c2005-10-30 15:01:51 -0800704 printk(KERN_INFO "sx%d: port %d: Receiver timeout. Hardware problems ?\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705 board_No(bp), port_No(port));
706 func_exit();
707 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800708
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709 } else if (status & RCSR_BREAK) {
710 dprintk(SX_DEBUG_RX, "sx%d: port %d: Handling break...\n",
711 board_No(bp), port_No(port));
Alan Cox33f0f882006-01-09 20:54:13 -0800712 flag = TTY_BREAK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713 if (port->flags & ASYNC_SAK)
714 do_SAK(tty);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800715
716 } else if (status & RCSR_PE)
Alan Cox33f0f882006-01-09 20:54:13 -0800717 flag = TTY_PARITY;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800718
719 else if (status & RCSR_FE)
Alan Cox33f0f882006-01-09 20:54:13 -0800720 flag = TTY_FRAME;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800721
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722 else if (status & RCSR_OE)
Alan Cox33f0f882006-01-09 20:54:13 -0800723 flag = TTY_OVERRUN;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800724
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725 else
Alan Cox33f0f882006-01-09 20:54:13 -0800726 flag = TTY_NORMAL;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800727
Alan Cox33f0f882006-01-09 20:54:13 -0800728 if(tty_insert_flip_char(tty, ch, flag))
729 tty_flip_buffer_push(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730 func_exit();
731}
732
733
734static inline void sx_receive(struct specialix_board * bp)
735{
736 struct specialix_port *port;
737 struct tty_struct *tty;
738 unsigned char count;
739
740 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -0800741
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742 if (!(port = sx_get_port(bp, "Receive"))) {
743 dprintk (SX_DEBUG_RX, "Hmm, couldn't find port.\n");
744 func_exit();
745 return;
746 }
747 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800748
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749 count = sx_in(bp, CD186x_RDCR);
750 dprintk (SX_DEBUG_RX, "port: %p: count: %d\n", port, count);
751 port->hits[count > 8 ? 9 : count]++;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800752
Alan Cox33f0f882006-01-09 20:54:13 -0800753 tty_buffer_request_room(tty, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754
Alan Cox33f0f882006-01-09 20:54:13 -0800755 while (count--)
756 tty_insert_flip_char(tty, sx_in(bp, CD186x_RDR), TTY_NORMAL);
757 tty_flip_buffer_push(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758 func_exit();
759}
760
761
762static inline void sx_transmit(struct specialix_board * bp)
763{
764 struct specialix_port *port;
765 struct tty_struct *tty;
766 unsigned char count;
767
768 func_enter();
769 if (!(port = sx_get_port(bp, "Transmit"))) {
770 func_exit();
771 return;
772 }
773 dprintk (SX_DEBUG_TX, "port: %p\n", port);
774 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800775
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776 if (port->IER & IER_TXEMPTY) {
777 /* FIFO drained */
778 sx_out(bp, CD186x_CAR, port_No(port));
779 port->IER &= ~IER_TXEMPTY;
780 sx_out(bp, CD186x_IER, port->IER);
781 func_exit();
782 return;
783 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800784
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 if ((port->xmit_cnt <= 0 && !port->break_length)
786 || tty->stopped || tty->hw_stopped) {
787 sx_out(bp, CD186x_CAR, port_No(port));
788 port->IER &= ~IER_TXRDY;
789 sx_out(bp, CD186x_IER, port->IER);
790 func_exit();
791 return;
792 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800793
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 if (port->break_length) {
795 if (port->break_length > 0) {
796 if (port->COR2 & COR2_ETC) {
797 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
798 sx_out(bp, CD186x_TDR, CD186x_C_SBRK);
799 port->COR2 &= ~COR2_ETC;
800 }
801 count = min_t(int, port->break_length, 0xff);
802 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
803 sx_out(bp, CD186x_TDR, CD186x_C_DELAY);
804 sx_out(bp, CD186x_TDR, count);
805 if (!(port->break_length -= count))
806 port->break_length--;
807 } else {
808 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
809 sx_out(bp, CD186x_TDR, CD186x_C_EBRK);
810 sx_out(bp, CD186x_COR2, port->COR2);
811 sx_wait_CCR(bp);
812 sx_out(bp, CD186x_CCR, CCR_CORCHG2);
813 port->break_length = 0;
814 }
815
816 func_exit();
817 return;
818 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800819
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820 count = CD186x_NFIFO;
821 do {
822 sx_out(bp, CD186x_TDR, port->xmit_buf[port->xmit_tail++]);
823 port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1);
824 if (--port->xmit_cnt <= 0)
825 break;
826 } while (--count > 0);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800827
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 if (port->xmit_cnt <= 0) {
829 sx_out(bp, CD186x_CAR, port_No(port));
830 port->IER &= ~IER_TXRDY;
831 sx_out(bp, CD186x_IER, port->IER);
832 }
833 if (port->xmit_cnt <= port->wakeup_chars)
834 sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);
835
836 func_exit();
837}
838
839
840static inline void sx_check_modem(struct specialix_board * bp)
841{
842 struct specialix_port *port;
843 struct tty_struct *tty;
844 unsigned char mcr;
845 int msvr_cd;
846
847 dprintk (SX_DEBUG_SIGNALS, "Modem intr. ");
848 if (!(port = sx_get_port(bp, "Modem")))
849 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800850
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800852
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853 mcr = sx_in(bp, CD186x_MCR);
854 printk ("mcr = %02x.\n", mcr);
855
856 if ((mcr & MCR_CDCHG)) {
857 dprintk (SX_DEBUG_SIGNALS, "CD just changed... ");
858 msvr_cd = sx_in(bp, CD186x_MSVR) & MSVR_CD;
859 if (msvr_cd) {
860 dprintk (SX_DEBUG_SIGNALS, "Waking up guys in open.\n");
861 wake_up_interruptible(&port->open_wait);
862 } else {
863 dprintk (SX_DEBUG_SIGNALS, "Sending HUP.\n");
864 schedule_work(&port->tqueue_hangup);
865 }
866 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800867
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
869 if (mcr & MCR_CTSCHG) {
870 if (sx_in(bp, CD186x_MSVR) & MSVR_CTS) {
871 tty->hw_stopped = 0;
872 port->IER |= IER_TXRDY;
873 if (port->xmit_cnt <= port->wakeup_chars)
874 sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);
875 } else {
876 tty->hw_stopped = 1;
877 port->IER &= ~IER_TXRDY;
878 }
879 sx_out(bp, CD186x_IER, port->IER);
880 }
881 if (mcr & MCR_DSSXHG) {
882 if (sx_in(bp, CD186x_MSVR) & MSVR_DSR) {
883 tty->hw_stopped = 0;
884 port->IER |= IER_TXRDY;
885 if (port->xmit_cnt <= port->wakeup_chars)
886 sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);
887 } else {
888 tty->hw_stopped = 1;
889 port->IER &= ~IER_TXRDY;
890 }
891 sx_out(bp, CD186x_IER, port->IER);
892 }
893#endif /* SPECIALIX_BRAIN_DAMAGED_CTS */
Jeff Garzikd61780c2005-10-30 15:01:51 -0800894
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895 /* Clear change bits */
896 sx_out(bp, CD186x_MCR, 0);
897}
898
899
900/* The main interrupt processing routine */
901static irqreturn_t sx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
902{
903 unsigned char status;
904 unsigned char ack;
905 struct specialix_board *bp;
906 unsigned long loop = 0;
907 int saved_reg;
908 unsigned long flags;
909
910 func_enter();
911
912 bp = dev_id;
913 spin_lock_irqsave(&bp->lock, flags);
914
915 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);
916 if (!bp || !(bp->flags & SX_BOARD_ACTIVE)) {
917 dprintk (SX_DEBUG_IRQ, "sx: False interrupt. irq %d.\n", irq);
918 spin_unlock_irqrestore(&bp->lock, flags);
919 func_exit();
920 return IRQ_NONE;
921 }
922
923 saved_reg = bp->reg;
924
925 while ((++loop < 16) && (status = (sx_in(bp, CD186x_SRSR) &
926 (SRSR_RREQint |
927 SRSR_TREQint |
Jeff Garzikd61780c2005-10-30 15:01:51 -0800928 SRSR_MREQint)))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700929 if (status & SRSR_RREQint) {
930 ack = sx_in(bp, CD186x_RRAR);
931
932 if (ack == (SX_ID | GIVR_IT_RCV))
933 sx_receive(bp);
934 else if (ack == (SX_ID | GIVR_IT_REXC))
935 sx_receive_exc(bp);
936 else
937 printk(KERN_ERR "sx%d: status: 0x%x Bad receive ack 0x%02x.\n",
938 board_No(bp), status, ack);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800939
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940 } else if (status & SRSR_TREQint) {
941 ack = sx_in(bp, CD186x_TRAR);
942
943 if (ack == (SX_ID | GIVR_IT_TX))
944 sx_transmit(bp);
945 else
946 printk(KERN_ERR "sx%d: status: 0x%x Bad transmit ack 0x%02x. port: %d\n",
947 board_No(bp), status, ack, port_No (sx_get_port (bp, "Int")));
948 } else if (status & SRSR_MREQint) {
949 ack = sx_in(bp, CD186x_MRAR);
950
Jeff Garzikd61780c2005-10-30 15:01:51 -0800951 if (ack == (SX_ID | GIVR_IT_MODEM))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700952 sx_check_modem(bp);
953 else
954 printk(KERN_ERR "sx%d: status: 0x%x Bad modem ack 0x%02x.\n",
955 board_No(bp), status, ack);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800956
957 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958
959 sx_out(bp, CD186x_EOIR, 0); /* Mark end of interrupt */
960 }
961 bp->reg = saved_reg;
962 outb (bp->reg, bp->base + SX_ADDR_REG);
963 spin_unlock_irqrestore(&bp->lock, flags);
964 func_exit();
965 return IRQ_HANDLED;
966}
967
968
969/*
970 * Routines for open & close processing.
971 */
972
973static void turn_ints_off (struct specialix_board *bp)
974{
975 unsigned long flags;
976
977 func_enter();
978 if (bp->flags & SX_BOARD_IS_PCI) {
979 /* This was intended for enabeling the interrupt on the
980 * PCI card. However it seems that it's already enabled
981 * and as PCI interrupts can be shared, there is no real
982 * reason to have to turn it off. */
983 }
984
985 spin_lock_irqsave(&bp->lock, flags);
986 (void) sx_in_off (bp, 0); /* Turn off interrupts. */
987 spin_unlock_irqrestore(&bp->lock, flags);
988
989 func_exit();
990}
991
992static void turn_ints_on (struct specialix_board *bp)
993{
994 unsigned long flags;
995
996 func_enter();
997
998 if (bp->flags & SX_BOARD_IS_PCI) {
999 /* play with the PCI chip. See comment above. */
1000 }
1001 spin_lock_irqsave(&bp->lock, flags);
1002 (void) sx_in (bp, 0); /* Turn ON interrupts. */
1003 spin_unlock_irqrestore(&bp->lock, flags);
1004
1005 func_exit();
1006}
1007
1008
1009/* Called with disabled interrupts */
1010static inline int sx_setup_board(struct specialix_board * bp)
1011{
1012 int error;
1013
Jeff Garzikd61780c2005-10-30 15:01:51 -08001014 if (bp->flags & SX_BOARD_ACTIVE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015 return 0;
1016
1017 if (bp->flags & SX_BOARD_IS_PCI)
Thomas Gleixner0f2ed4c2006-07-01 19:29:33 -07001018 error = request_irq(bp->irq, sx_interrupt, IRQF_DISABLED | IRQF_SHARED, "specialix IO8+", bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019 else
Thomas Gleixner0f2ed4c2006-07-01 19:29:33 -07001020 error = request_irq(bp->irq, sx_interrupt, IRQF_DISABLED, "specialix IO8+", bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021
Jeff Garzikd61780c2005-10-30 15:01:51 -08001022 if (error)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001023 return error;
1024
1025 turn_ints_on (bp);
1026 bp->flags |= SX_BOARD_ACTIVE;
1027
1028 return 0;
1029}
1030
1031
1032/* Called with disabled interrupts */
1033static inline void sx_shutdown_board(struct specialix_board *bp)
1034{
1035 func_enter();
1036
1037 if (!(bp->flags & SX_BOARD_ACTIVE)) {
1038 func_exit();
1039 return;
1040 }
1041
1042 bp->flags &= ~SX_BOARD_ACTIVE;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001043
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044 dprintk (SX_DEBUG_IRQ, "Freeing IRQ%d for board %d.\n",
1045 bp->irq, board_No (bp));
1046 free_irq(bp->irq, bp);
1047
1048 turn_ints_off (bp);
1049
1050
1051 func_exit();
1052}
1053
1054
1055/*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001056 * Setting up port characteristics.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057 * Must be called with disabled interrupts
1058 */
1059static void sx_change_speed(struct specialix_board *bp, struct specialix_port *port)
1060{
1061 struct tty_struct *tty;
1062 unsigned long baud;
1063 long tmp;
1064 unsigned char cor1 = 0, cor3 = 0;
1065 unsigned char mcor1 = 0, mcor2 = 0;
1066 static unsigned long again;
1067 unsigned long flags;
1068
1069 func_enter();
1070
1071 if (!(tty = port->tty) || !tty->termios) {
1072 func_exit();
1073 return;
1074 }
1075
1076 port->IER = 0;
1077 port->COR2 = 0;
1078 /* Select port on the board */
1079 spin_lock_irqsave(&bp->lock, flags);
1080 sx_out(bp, CD186x_CAR, port_No(port));
1081
1082 /* The Specialix board doens't implement the RTS lines.
1083 They are used to set the IRQ level. Don't touch them. */
1084 if (SX_CRTSCTS(tty))
1085 port->MSVR = MSVR_DTR | (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
1086 else
1087 port->MSVR = (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
1088 spin_unlock_irqrestore(&bp->lock, flags);
1089 dprintk (SX_DEBUG_TERMIOS, "sx: got MSVR=%02x.\n", port->MSVR);
1090 baud = C_BAUD(tty);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001091
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092 if (baud & CBAUDEX) {
1093 baud &= ~CBAUDEX;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001094 if (baud < 1 || baud > 2)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095 port->tty->termios->c_cflag &= ~CBAUDEX;
1096 else
1097 baud += 15;
1098 }
1099 if (baud == 15) {
1100 if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
1101 baud ++;
1102 if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
1103 baud += 2;
1104 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001105
1106
Linus Torvalds1da177e2005-04-16 15:20:36 -07001107 if (!baud_table[baud]) {
1108 /* Drop DTR & exit */
1109 dprintk (SX_DEBUG_TERMIOS, "Dropping DTR... Hmm....\n");
1110 if (!SX_CRTSCTS (tty)) {
1111 port -> MSVR &= ~ MSVR_DTR;
1112 spin_lock_irqsave(&bp->lock, flags);
1113 sx_out(bp, CD186x_MSVR, port->MSVR );
1114 spin_unlock_irqrestore(&bp->lock, flags);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001115 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116 else
1117 dprintk (SX_DEBUG_TERMIOS, "Can't drop DTR: no DTR.\n");
1118 return;
1119 } else {
1120 /* Set DTR on */
1121 if (!SX_CRTSCTS (tty)) {
1122 port ->MSVR |= MSVR_DTR;
1123 }
1124 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001125
Linus Torvalds1da177e2005-04-16 15:20:36 -07001126 /*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001127 * Now we must calculate some speed depended things
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128 */
1129
1130 /* Set baud rate for port */
1131 tmp = port->custom_divisor ;
1132 if ( tmp )
1133 printk (KERN_INFO "sx%d: Using custom baud rate divisor %ld. \n"
1134 "This is an untested option, please be carefull.\n",
1135 port_No (port), tmp);
1136 else
1137 tmp = (((SX_OSCFREQ + baud_table[baud]/2) / baud_table[baud] +
1138 CD186x_TPC/2) / CD186x_TPC);
1139
Jeff Garzikd61780c2005-10-30 15:01:51 -08001140 if ((tmp < 0x10) && time_before(again, jiffies)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141 again = jiffies + HZ * 60;
1142 /* Page 48 of version 2.0 of the CL-CD1865 databook */
1143 if (tmp >= 12) {
1144 printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
1145 "Performance degradation is possible.\n"
1146 "Read specialix.txt for more info.\n",
1147 port_No (port), tmp);
1148 } else {
1149 printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
1150 "Warning: overstressing Cirrus chip. "
1151 "This might not work.\n"
Jeff Garzikd61780c2005-10-30 15:01:51 -08001152 "Read specialix.txt for more info.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153 port_No (port), tmp);
1154 }
1155 }
1156 spin_lock_irqsave(&bp->lock, flags);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001157 sx_out(bp, CD186x_RBPRH, (tmp >> 8) & 0xff);
1158 sx_out(bp, CD186x_TBPRH, (tmp >> 8) & 0xff);
1159 sx_out(bp, CD186x_RBPRL, tmp & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 sx_out(bp, CD186x_TBPRL, tmp & 0xff);
1161 spin_unlock_irqrestore(&bp->lock, flags);
1162 if (port->custom_divisor) {
1163 baud = (SX_OSCFREQ + port->custom_divisor/2) / port->custom_divisor;
1164 baud = ( baud + 5 ) / 10;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001165 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166 baud = (baud_table[baud] + 5) / 10; /* Estimated CPS */
1167
1168 /* Two timer ticks seems enough to wakeup something like SLIP driver */
Jeff Garzikd61780c2005-10-30 15:01:51 -08001169 tmp = ((baud + HZ/2) / HZ) * 2 - CD186x_NFIFO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001170 port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ?
1171 SERIAL_XMIT_SIZE - 1 : tmp);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001172
Linus Torvalds1da177e2005-04-16 15:20:36 -07001173 /* Receiver timeout will be transmission time for 1.5 chars */
1174 tmp = (SPECIALIX_TPS + SPECIALIX_TPS/2 + baud/2) / baud;
1175 tmp = (tmp > 0xff) ? 0xff : tmp;
1176 spin_lock_irqsave(&bp->lock, flags);
1177 sx_out(bp, CD186x_RTPR, tmp);
1178 spin_unlock_irqrestore(&bp->lock, flags);
1179 switch (C_CSIZE(tty)) {
1180 case CS5:
1181 cor1 |= COR1_5BITS;
1182 break;
1183 case CS6:
1184 cor1 |= COR1_6BITS;
1185 break;
1186 case CS7:
1187 cor1 |= COR1_7BITS;
1188 break;
1189 case CS8:
1190 cor1 |= COR1_8BITS;
1191 break;
1192 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001193
1194 if (C_CSTOPB(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195 cor1 |= COR1_2SB;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001196
Linus Torvalds1da177e2005-04-16 15:20:36 -07001197 cor1 |= COR1_IGNORE;
1198 if (C_PARENB(tty)) {
1199 cor1 |= COR1_NORMPAR;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001200 if (C_PARODD(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001201 cor1 |= COR1_ODDP;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001202 if (I_INPCK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001203 cor1 &= ~COR1_IGNORE;
1204 }
1205 /* Set marking of some errors */
1206 port->mark_mask = RCSR_OE | RCSR_TOUT;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001207 if (I_INPCK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208 port->mark_mask |= RCSR_FE | RCSR_PE;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001209 if (I_BRKINT(tty) || I_PARMRK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210 port->mark_mask |= RCSR_BREAK;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001211 if (I_IGNPAR(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212 port->mark_mask &= ~(RCSR_FE | RCSR_PE);
1213 if (I_IGNBRK(tty)) {
1214 port->mark_mask &= ~RCSR_BREAK;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001215 if (I_IGNPAR(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001216 /* Real raw mode. Ignore all */
1217 port->mark_mask &= ~RCSR_OE;
1218 }
1219 /* Enable Hardware Flow Control */
1220 if (C_CRTSCTS(tty)) {
1221#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
1222 port->IER |= IER_DSR | IER_CTS;
1223 mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD;
1224 mcor2 |= MCOR2_DSROD | MCOR2_CTSOD;
1225 spin_lock_irqsave(&bp->lock, flags);
1226 tty->hw_stopped = !(sx_in(bp, CD186x_MSVR) & (MSVR_CTS|MSVR_DSR));
1227 spin_unlock_irqrestore(&bp->lock, flags);
1228#else
Jeff Garzikd61780c2005-10-30 15:01:51 -08001229 port->COR2 |= COR2_CTSAE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001230#endif
1231 }
1232 /* Enable Software Flow Control. FIXME: I'm not sure about this */
1233 /* Some people reported that it works, but I still doubt it */
1234 if (I_IXON(tty)) {
1235 port->COR2 |= COR2_TXIBE;
1236 cor3 |= (COR3_FCT | COR3_SCDE);
1237 if (I_IXANY(tty))
1238 port->COR2 |= COR2_IXM;
1239 spin_lock_irqsave(&bp->lock, flags);
1240 sx_out(bp, CD186x_SCHR1, START_CHAR(tty));
1241 sx_out(bp, CD186x_SCHR2, STOP_CHAR(tty));
1242 sx_out(bp, CD186x_SCHR3, START_CHAR(tty));
1243 sx_out(bp, CD186x_SCHR4, STOP_CHAR(tty));
1244 spin_unlock_irqrestore(&bp->lock, flags);
1245 }
1246 if (!C_CLOCAL(tty)) {
1247 /* Enable CD check */
1248 port->IER |= IER_CD;
1249 mcor1 |= MCOR1_CDZD;
1250 mcor2 |= MCOR2_CDOD;
1251 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001252
1253 if (C_CREAD(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001254 /* Enable receiver */
1255 port->IER |= IER_RXD;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001256
Linus Torvalds1da177e2005-04-16 15:20:36 -07001257 /* Set input FIFO size (1-8 bytes) */
1258 cor3 |= sx_rxfifo;
1259 /* Setting up CD186x channel registers */
1260 spin_lock_irqsave(&bp->lock, flags);
1261 sx_out(bp, CD186x_COR1, cor1);
1262 sx_out(bp, CD186x_COR2, port->COR2);
1263 sx_out(bp, CD186x_COR3, cor3);
1264 spin_unlock_irqrestore(&bp->lock, flags);
1265 /* Make CD186x know about registers change */
1266 sx_wait_CCR(bp);
1267 spin_lock_irqsave(&bp->lock, flags);
1268 sx_out(bp, CD186x_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3);
1269 /* Setting up modem option registers */
1270 dprintk (SX_DEBUG_TERMIOS, "Mcor1 = %02x, mcor2 = %02x.\n", mcor1, mcor2);
1271 sx_out(bp, CD186x_MCOR1, mcor1);
1272 sx_out(bp, CD186x_MCOR2, mcor2);
1273 spin_unlock_irqrestore(&bp->lock, flags);
1274 /* Enable CD186x transmitter & receiver */
1275 sx_wait_CCR(bp);
1276 spin_lock_irqsave(&bp->lock, flags);
1277 sx_out(bp, CD186x_CCR, CCR_TXEN | CCR_RXEN);
1278 /* Enable interrupts */
1279 sx_out(bp, CD186x_IER, port->IER);
1280 /* And finally set the modem lines... */
1281 sx_out(bp, CD186x_MSVR, port->MSVR);
1282 spin_unlock_irqrestore(&bp->lock, flags);
1283
1284 func_exit();
1285}
1286
1287
1288/* Must be called with interrupts enabled */
1289static int sx_setup_port(struct specialix_board *bp, struct specialix_port *port)
1290{
1291 unsigned long flags;
1292
1293 func_enter();
1294
1295 if (port->flags & ASYNC_INITIALIZED) {
1296 func_exit();
1297 return 0;
1298 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001299
Linus Torvalds1da177e2005-04-16 15:20:36 -07001300 if (!port->xmit_buf) {
1301 /* We may sleep in get_zeroed_page() */
1302 unsigned long tmp;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001303
Linus Torvalds1da177e2005-04-16 15:20:36 -07001304 if (!(tmp = get_zeroed_page(GFP_KERNEL))) {
1305 func_exit();
1306 return -ENOMEM;
1307 }
1308
1309 if (port->xmit_buf) {
1310 free_page(tmp);
1311 func_exit();
1312 return -ERESTARTSYS;
1313 }
1314 port->xmit_buf = (unsigned char *) tmp;
1315 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001316
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317 spin_lock_irqsave(&port->lock, flags);
1318
Jeff Garzikd61780c2005-10-30 15:01:51 -08001319 if (port->tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001320 clear_bit(TTY_IO_ERROR, &port->tty->flags);
1321
1322 port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
1323 sx_change_speed(bp, port);
1324 port->flags |= ASYNC_INITIALIZED;
1325
1326 spin_unlock_irqrestore(&port->lock, flags);
1327
Jeff Garzikd61780c2005-10-30 15:01:51 -08001328
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329 func_exit();
1330 return 0;
1331}
1332
1333
1334/* Must be called with interrupts disabled */
1335static void sx_shutdown_port(struct specialix_board *bp, struct specialix_port *port)
1336{
1337 struct tty_struct *tty;
1338 int i;
1339 unsigned long flags;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001340
Linus Torvalds1da177e2005-04-16 15:20:36 -07001341 func_enter();
1342
1343 if (!(port->flags & ASYNC_INITIALIZED)) {
1344 func_exit();
1345 return;
1346 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001347
Linus Torvalds1da177e2005-04-16 15:20:36 -07001348 if (sx_debug & SX_DEBUG_FIFO) {
1349 dprintk(SX_DEBUG_FIFO, "sx%d: port %d: %ld overruns, FIFO hits [ ",
1350 board_No(bp), port_No(port), port->overrun);
1351 for (i = 0; i < 10; i++) {
1352 dprintk(SX_DEBUG_FIFO, "%ld ", port->hits[i]);
1353 }
1354 dprintk(SX_DEBUG_FIFO, "].\n");
1355 }
1356
1357 if (port->xmit_buf) {
1358 free_page((unsigned long) port->xmit_buf);
1359 port->xmit_buf = NULL;
1360 }
1361
1362 /* Select port */
1363 spin_lock_irqsave(&bp->lock, flags);
1364 sx_out(bp, CD186x_CAR, port_No(port));
1365
1366 if (!(tty = port->tty) || C_HUPCL(tty)) {
1367 /* Drop DTR */
1368 sx_out(bp, CD186x_MSVDTR, 0);
1369 }
1370 spin_unlock_irqrestore(&bp->lock, flags);
1371 /* Reset port */
1372 sx_wait_CCR(bp);
1373 spin_lock_irqsave(&bp->lock, flags);
1374 sx_out(bp, CD186x_CCR, CCR_SOFTRESET);
1375 /* Disable all interrupts from this port */
1376 port->IER = 0;
1377 sx_out(bp, CD186x_IER, port->IER);
1378 spin_unlock_irqrestore(&bp->lock, flags);
1379 if (tty)
1380 set_bit(TTY_IO_ERROR, &tty->flags);
1381 port->flags &= ~ASYNC_INITIALIZED;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001382
1383 if (!bp->count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384 sx_shutdown_board(bp);
1385 func_exit();
1386}
1387
Jeff Garzikd61780c2005-10-30 15:01:51 -08001388
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389static int block_til_ready(struct tty_struct *tty, struct file * filp,
1390 struct specialix_port *port)
1391{
1392 DECLARE_WAITQUEUE(wait, current);
1393 struct specialix_board *bp = port_Board(port);
1394 int retval;
1395 int do_clocal = 0;
1396 int CD;
1397 unsigned long flags;
1398
1399 func_enter();
1400
1401 /*
1402 * If the device is in the middle of being closed, then block
1403 * until it's done, and then try again.
1404 */
1405 if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {
1406 interruptible_sleep_on(&port->close_wait);
1407 if (port->flags & ASYNC_HUP_NOTIFY) {
1408 func_exit();
1409 return -EAGAIN;
1410 } else {
1411 func_exit();
1412 return -ERESTARTSYS;
1413 }
1414 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001415
Linus Torvalds1da177e2005-04-16 15:20:36 -07001416 /*
1417 * If non-blocking mode is set, or the port is not enabled,
1418 * then make the check up front and then exit.
1419 */
1420 if ((filp->f_flags & O_NONBLOCK) ||
1421 (tty->flags & (1 << TTY_IO_ERROR))) {
1422 port->flags |= ASYNC_NORMAL_ACTIVE;
1423 func_exit();
1424 return 0;
1425 }
1426
1427 if (C_CLOCAL(tty))
1428 do_clocal = 1;
1429
1430 /*
1431 * Block waiting for the carrier detect and the line to become
1432 * free (i.e., not in use by the callout). While we are in
1433 * this loop, info->count is dropped by one, so that
1434 * rs_close() knows when to free things. We restore it upon
1435 * exit, either normal or abnormal.
1436 */
1437 retval = 0;
1438 add_wait_queue(&port->open_wait, &wait);
1439 spin_lock_irqsave(&port->lock, flags);
1440 if (!tty_hung_up_p(filp)) {
1441 port->count--;
1442 }
1443 spin_unlock_irqrestore(&port->lock, flags);
1444 port->blocked_open++;
1445 while (1) {
1446 spin_lock_irqsave(&bp->lock, flags);
1447 sx_out(bp, CD186x_CAR, port_No(port));
1448 CD = sx_in(bp, CD186x_MSVR) & MSVR_CD;
1449 if (SX_CRTSCTS (tty)) {
1450 /* Activate RTS */
1451 port->MSVR |= MSVR_DTR; /* WTF? */
1452 sx_out (bp, CD186x_MSVR, port->MSVR);
1453 } else {
1454 /* Activate DTR */
1455 port->MSVR |= MSVR_DTR;
1456 sx_out (bp, CD186x_MSVR, port->MSVR);
1457 }
1458 spin_unlock_irqrestore(&bp->lock, flags);
1459 set_current_state(TASK_INTERRUPTIBLE);
1460 if (tty_hung_up_p(filp) ||
1461 !(port->flags & ASYNC_INITIALIZED)) {
1462 if (port->flags & ASYNC_HUP_NOTIFY)
1463 retval = -EAGAIN;
1464 else
Jeff Garzikd61780c2005-10-30 15:01:51 -08001465 retval = -ERESTARTSYS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001466 break;
1467 }
1468 if (!(port->flags & ASYNC_CLOSING) &&
1469 (do_clocal || CD))
1470 break;
1471 if (signal_pending(current)) {
1472 retval = -ERESTARTSYS;
1473 break;
1474 }
1475 schedule();
1476 }
1477
1478 set_current_state(TASK_RUNNING);
1479 remove_wait_queue(&port->open_wait, &wait);
1480 spin_lock_irqsave(&port->lock, flags);
1481 if (!tty_hung_up_p(filp)) {
1482 port->count++;
1483 }
1484 port->blocked_open--;
1485 spin_unlock_irqrestore(&port->lock, flags);
1486 if (retval) {
1487 func_exit();
1488 return retval;
1489 }
1490
1491 port->flags |= ASYNC_NORMAL_ACTIVE;
1492 func_exit();
1493 return 0;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001494}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001495
1496
1497static int sx_open(struct tty_struct * tty, struct file * filp)
1498{
1499 int board;
1500 int error;
1501 struct specialix_port * port;
1502 struct specialix_board * bp;
1503 int i;
1504 unsigned long flags;
1505
1506 func_enter();
1507
1508 board = SX_BOARD(tty->index);
1509
1510 if (board >= SX_NBOARD || !(sx_board[board].flags & SX_BOARD_PRESENT)) {
1511 func_exit();
1512 return -ENODEV;
1513 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001514
Linus Torvalds1da177e2005-04-16 15:20:36 -07001515 bp = &sx_board[board];
1516 port = sx_port + board * SX_NPORT + SX_PORT(tty->index);
1517 port->overrun = 0;
1518 for (i = 0; i < 10; i++)
1519 port->hits[i]=0;
1520
1521 dprintk (SX_DEBUG_OPEN, "Board = %d, bp = %p, port = %p, portno = %d.\n",
1522 board, bp, port, SX_PORT(tty->index));
1523
1524 if (sx_paranoia_check(port, tty->name, "sx_open")) {
1525 func_enter();
1526 return -ENODEV;
1527 }
1528
1529 if ((error = sx_setup_board(bp))) {
1530 func_exit();
1531 return error;
1532 }
1533
1534 spin_lock_irqsave(&bp->lock, flags);
1535 port->count++;
1536 bp->count++;
1537 tty->driver_data = port;
1538 port->tty = tty;
1539 spin_unlock_irqrestore(&bp->lock, flags);
1540
1541 if ((error = sx_setup_port(bp, port))) {
1542 func_enter();
1543 return error;
1544 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001545
Linus Torvalds1da177e2005-04-16 15:20:36 -07001546 if ((error = block_til_ready(tty, filp, port))) {
1547 func_enter();
1548 return error;
1549 }
1550
1551 func_exit();
1552 return 0;
1553}
1554
1555
1556static void sx_close(struct tty_struct * tty, struct file * filp)
1557{
1558 struct specialix_port *port = (struct specialix_port *) tty->driver_data;
1559 struct specialix_board *bp;
1560 unsigned long flags;
1561 unsigned long timeout;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001562
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563 func_enter();
1564 if (!port || sx_paranoia_check(port, tty->name, "close")) {
1565 func_exit();
1566 return;
1567 }
1568 spin_lock_irqsave(&port->lock, flags);
1569
1570 if (tty_hung_up_p(filp)) {
1571 spin_unlock_irqrestore(&port->lock, flags);
1572 func_exit();
1573 return;
1574 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001575
Linus Torvalds1da177e2005-04-16 15:20:36 -07001576 bp = port_Board(port);
1577 if ((tty->count == 1) && (port->count != 1)) {
1578 printk(KERN_ERR "sx%d: sx_close: bad port count;"
1579 " tty->count is 1, port count is %d\n",
1580 board_No(bp), port->count);
1581 port->count = 1;
1582 }
1583
1584 if (port->count > 1) {
1585 port->count--;
1586 bp->count--;
1587
1588 spin_unlock_irqrestore(&port->lock, flags);
1589
1590 func_exit();
1591 return;
1592 }
1593 port->flags |= ASYNC_CLOSING;
1594 /*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001595 * Now we wait for the transmit buffer to clear; and we notify
Linus Torvalds1da177e2005-04-16 15:20:36 -07001596 * the line discipline to only process XON/XOFF characters.
1597 */
1598 tty->closing = 1;
1599 spin_unlock_irqrestore(&port->lock, flags);
1600 dprintk (SX_DEBUG_OPEN, "Closing\n");
1601 if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) {
1602 tty_wait_until_sent(tty, port->closing_wait);
1603 }
1604 /*
1605 * At this point we stop accepting input. To do this, we
1606 * disable the receive line status interrupts, and tell the
1607 * interrupt driver to stop checking the data ready bit in the
1608 * line status register.
1609 */
1610 dprintk (SX_DEBUG_OPEN, "Closed\n");
1611 port->IER &= ~IER_RXD;
1612 if (port->flags & ASYNC_INITIALIZED) {
1613 port->IER &= ~IER_TXRDY;
1614 port->IER |= IER_TXEMPTY;
1615 spin_lock_irqsave(&bp->lock, flags);
1616 sx_out(bp, CD186x_CAR, port_No(port));
1617 sx_out(bp, CD186x_IER, port->IER);
1618 spin_unlock_irqrestore(&bp->lock, flags);
1619 /*
1620 * Before we drop DTR, make sure the UART transmitter
1621 * has completely drained; this is especially
1622 * important if there is a transmit FIFO!
1623 */
1624 timeout = jiffies+HZ;
1625 while(port->IER & IER_TXEMPTY) {
1626 set_current_state (TASK_INTERRUPTIBLE);
1627 msleep_interruptible(jiffies_to_msecs(port->timeout));
1628 if (time_after(jiffies, timeout)) {
1629 printk (KERN_INFO "Timeout waiting for close\n");
1630 break;
1631 }
1632 }
1633
1634 }
1635
1636 if (--bp->count < 0) {
1637 printk(KERN_ERR "sx%d: sx_shutdown_port: bad board count: %d port: %d\n",
1638 board_No(bp), bp->count, tty->index);
1639 bp->count = 0;
1640 }
1641 if (--port->count < 0) {
1642 printk(KERN_ERR "sx%d: sx_close: bad port count for tty%d: %d\n",
1643 board_No(bp), port_No(port), port->count);
1644 port->count = 0;
1645 }
1646
1647 sx_shutdown_port(bp, port);
1648 if (tty->driver->flush_buffer)
1649 tty->driver->flush_buffer(tty);
1650 tty_ldisc_flush(tty);
1651 spin_lock_irqsave(&port->lock, flags);
1652 tty->closing = 0;
1653 port->event = 0;
1654 port->tty = NULL;
1655 spin_unlock_irqrestore(&port->lock, flags);
1656 if (port->blocked_open) {
1657 if (port->close_delay) {
1658 msleep_interruptible(jiffies_to_msecs(port->close_delay));
1659 }
1660 wake_up_interruptible(&port->open_wait);
1661 }
1662 port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
1663 wake_up_interruptible(&port->close_wait);
1664
1665 func_exit();
1666}
1667
1668
Jeff Garzikd61780c2005-10-30 15:01:51 -08001669static int sx_write(struct tty_struct * tty,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001670 const unsigned char *buf, int count)
1671{
1672 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1673 struct specialix_board *bp;
1674 int c, total = 0;
1675 unsigned long flags;
1676
1677 func_enter();
1678 if (sx_paranoia_check(port, tty->name, "sx_write")) {
1679 func_exit();
1680 return 0;
1681 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001682
Linus Torvalds1da177e2005-04-16 15:20:36 -07001683 bp = port_Board(port);
1684
Eric Sesterhenn326f28e92006-06-25 05:48:48 -07001685 if (!port->xmit_buf || !tmp_buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001686 func_exit();
1687 return 0;
1688 }
1689
1690 while (1) {
1691 spin_lock_irqsave(&port->lock, flags);
1692 c = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
1693 SERIAL_XMIT_SIZE - port->xmit_head));
1694 if (c <= 0) {
1695 spin_unlock_irqrestore(&port->lock, flags);
1696 break;
1697 }
1698 memcpy(port->xmit_buf + port->xmit_head, buf, c);
1699 port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
1700 port->xmit_cnt += c;
1701 spin_unlock_irqrestore(&port->lock, flags);
1702
1703 buf += c;
1704 count -= c;
1705 total += c;
1706 }
1707
1708 spin_lock_irqsave(&bp->lock, flags);
1709 if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
1710 !(port->IER & IER_TXRDY)) {
1711 port->IER |= IER_TXRDY;
1712 sx_out(bp, CD186x_CAR, port_No(port));
1713 sx_out(bp, CD186x_IER, port->IER);
1714 }
1715 spin_unlock_irqrestore(&bp->lock, flags);
1716 func_exit();
1717
1718 return total;
1719}
1720
1721
1722static void sx_put_char(struct tty_struct * tty, unsigned char ch)
1723{
1724 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1725 unsigned long flags;
1726 struct specialix_board * bp;
1727
1728 func_enter();
1729
1730 if (sx_paranoia_check(port, tty->name, "sx_put_char")) {
1731 func_exit();
1732 return;
1733 }
1734 dprintk (SX_DEBUG_TX, "check tty: %p %p\n", tty, port->xmit_buf);
Eric Sesterhenn326f28e92006-06-25 05:48:48 -07001735 if (!port->xmit_buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001736 func_exit();
1737 return;
1738 }
1739 bp = port_Board(port);
1740 spin_lock_irqsave(&port->lock, flags);
1741
1742 dprintk (SX_DEBUG_TX, "xmit_cnt: %d xmit_buf: %p\n", port->xmit_cnt, port->xmit_buf);
1743 if ((port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) || (!port->xmit_buf)) {
1744 spin_unlock_irqrestore(&port->lock, flags);
1745 dprintk (SX_DEBUG_TX, "Exit size\n");
1746 func_exit();
1747 return;
1748 }
1749 dprintk (SX_DEBUG_TX, "Handle xmit: %p %p\n", port, port->xmit_buf);
1750 port->xmit_buf[port->xmit_head++] = ch;
1751 port->xmit_head &= SERIAL_XMIT_SIZE - 1;
1752 port->xmit_cnt++;
1753 spin_unlock_irqrestore(&port->lock, flags);
1754
1755 func_exit();
1756}
1757
1758
1759static void sx_flush_chars(struct tty_struct * tty)
1760{
1761 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1762 unsigned long flags;
1763 struct specialix_board * bp = port_Board(port);
1764
1765 func_enter();
1766
1767 if (sx_paranoia_check(port, tty->name, "sx_flush_chars")) {
1768 func_exit();
1769 return;
1770 }
1771 if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
1772 !port->xmit_buf) {
1773 func_exit();
1774 return;
1775 }
1776 spin_lock_irqsave(&bp->lock, flags);
1777 port->IER |= IER_TXRDY;
1778 sx_out(port_Board(port), CD186x_CAR, port_No(port));
1779 sx_out(port_Board(port), CD186x_IER, port->IER);
1780 spin_unlock_irqrestore(&bp->lock, flags);
1781
1782 func_exit();
1783}
1784
1785
1786static int sx_write_room(struct tty_struct * tty)
1787{
1788 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1789 int ret;
1790
1791 func_enter();
1792
1793 if (sx_paranoia_check(port, tty->name, "sx_write_room")) {
1794 func_exit();
1795 return 0;
1796 }
1797
1798 ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;
1799 if (ret < 0)
1800 ret = 0;
1801
1802 func_exit();
1803 return ret;
1804}
1805
1806
1807static int sx_chars_in_buffer(struct tty_struct *tty)
1808{
1809 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1810
1811 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08001812
Linus Torvalds1da177e2005-04-16 15:20:36 -07001813 if (sx_paranoia_check(port, tty->name, "sx_chars_in_buffer")) {
1814 func_exit();
1815 return 0;
1816 }
1817 func_exit();
1818 return port->xmit_cnt;
1819}
1820
1821
1822static void sx_flush_buffer(struct tty_struct *tty)
1823{
1824 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1825 unsigned long flags;
1826 struct specialix_board * bp;
1827
1828 func_enter();
1829
1830 if (sx_paranoia_check(port, tty->name, "sx_flush_buffer")) {
1831 func_exit();
1832 return;
1833 }
1834
1835 bp = port_Board(port);
1836 spin_lock_irqsave(&port->lock, flags);
1837 port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
1838 spin_unlock_irqrestore(&port->lock, flags);
1839 tty_wakeup(tty);
1840
1841 func_exit();
1842}
1843
1844
1845static int sx_tiocmget(struct tty_struct *tty, struct file *file)
1846{
1847 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1848 struct specialix_board * bp;
1849 unsigned char status;
1850 unsigned int result;
1851 unsigned long flags;
1852
1853 func_enter();
1854
1855 if (sx_paranoia_check(port, tty->name, __FUNCTION__)) {
1856 func_exit();
1857 return -ENODEV;
1858 }
1859
1860 bp = port_Board(port);
1861 spin_lock_irqsave (&bp->lock, flags);
1862 sx_out(bp, CD186x_CAR, port_No(port));
1863 status = sx_in(bp, CD186x_MSVR);
1864 spin_unlock_irqrestore(&bp->lock, flags);
1865 dprintk (SX_DEBUG_INIT, "Got msvr[%d] = %02x, car = %d.\n",
1866 port_No(port), status, sx_in (bp, CD186x_CAR));
1867 dprintk (SX_DEBUG_INIT, "sx_port = %p, port = %p\n", sx_port, port);
1868 if (SX_CRTSCTS(port->tty)) {
Jeff Garzikd61780c2005-10-30 15:01:51 -08001869 result = /* (status & MSVR_RTS) ? */ TIOCM_DTR /* : 0) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001870 | ((status & MSVR_DTR) ? TIOCM_RTS : 0)
1871 | ((status & MSVR_CD) ? TIOCM_CAR : 0)
1872 |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */
1873 | ((status & MSVR_CTS) ? TIOCM_CTS : 0);
1874 } else {
Jeff Garzikd61780c2005-10-30 15:01:51 -08001875 result = /* (status & MSVR_RTS) ? */ TIOCM_RTS /* : 0) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001876 | ((status & MSVR_DTR) ? TIOCM_DTR : 0)
1877 | ((status & MSVR_CD) ? TIOCM_CAR : 0)
1878 |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */
1879 | ((status & MSVR_CTS) ? TIOCM_CTS : 0);
1880 }
1881
1882 func_exit();
1883
1884 return result;
1885}
1886
1887
1888static int sx_tiocmset(struct tty_struct *tty, struct file *file,
1889 unsigned int set, unsigned int clear)
1890{
1891 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1892 unsigned long flags;
1893 struct specialix_board *bp;
1894
1895 func_enter();
1896
1897 if (sx_paranoia_check(port, tty->name, __FUNCTION__)) {
1898 func_exit();
1899 return -ENODEV;
1900 }
1901
1902 bp = port_Board(port);
1903
1904 spin_lock_irqsave(&port->lock, flags);
1905 /* if (set & TIOCM_RTS)
1906 port->MSVR |= MSVR_RTS; */
1907 /* if (set & TIOCM_DTR)
1908 port->MSVR |= MSVR_DTR; */
1909
1910 if (SX_CRTSCTS(port->tty)) {
1911 if (set & TIOCM_RTS)
1912 port->MSVR |= MSVR_DTR;
1913 } else {
1914 if (set & TIOCM_DTR)
1915 port->MSVR |= MSVR_DTR;
1916 }
1917
1918 /* if (clear & TIOCM_RTS)
1919 port->MSVR &= ~MSVR_RTS; */
1920 /* if (clear & TIOCM_DTR)
1921 port->MSVR &= ~MSVR_DTR; */
1922 if (SX_CRTSCTS(port->tty)) {
1923 if (clear & TIOCM_RTS)
1924 port->MSVR &= ~MSVR_DTR;
1925 } else {
1926 if (clear & TIOCM_DTR)
1927 port->MSVR &= ~MSVR_DTR;
1928 }
1929 spin_lock_irqsave(&bp->lock, flags);
1930 sx_out(bp, CD186x_CAR, port_No(port));
1931 sx_out(bp, CD186x_MSVR, port->MSVR);
1932 spin_unlock_irqrestore(&bp->lock, flags);
1933 spin_unlock_irqrestore(&port->lock, flags);
1934 func_exit();
1935 return 0;
1936}
1937
1938
1939static inline void sx_send_break(struct specialix_port * port, unsigned long length)
1940{
1941 struct specialix_board *bp = port_Board(port);
1942 unsigned long flags;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001943
Linus Torvalds1da177e2005-04-16 15:20:36 -07001944 func_enter();
1945
1946 spin_lock_irqsave (&port->lock, flags);
1947 port->break_length = SPECIALIX_TPS / HZ * length;
1948 port->COR2 |= COR2_ETC;
1949 port->IER |= IER_TXRDY;
1950 spin_lock_irqsave(&bp->lock, flags);
1951 sx_out(bp, CD186x_CAR, port_No(port));
1952 sx_out(bp, CD186x_COR2, port->COR2);
1953 sx_out(bp, CD186x_IER, port->IER);
1954 spin_unlock_irqrestore(&bp->lock, flags);
1955 spin_unlock_irqrestore (&port->lock, flags);
1956 sx_wait_CCR(bp);
1957 spin_lock_irqsave(&bp->lock, flags);
1958 sx_out(bp, CD186x_CCR, CCR_CORCHG2);
1959 spin_unlock_irqrestore(&bp->lock, flags);
1960 sx_wait_CCR(bp);
1961
1962 func_exit();
1963}
1964
1965
1966static inline int sx_set_serial_info(struct specialix_port * port,
1967 struct serial_struct __user * newinfo)
1968{
1969 struct serial_struct tmp;
1970 struct specialix_board *bp = port_Board(port);
1971 int change_speed;
1972
1973 func_enter();
1974 /*
Jesper Juhle49332b2005-05-01 08:59:08 -07001975 if (!access_ok(VERIFY_READ, (void *) newinfo, sizeof(tmp))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001976 func_exit();
Jesper Juhle49332b2005-05-01 08:59:08 -07001977 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001978 }
1979 */
1980 if (copy_from_user(&tmp, newinfo, sizeof(tmp))) {
1981 func_enter();
1982 return -EFAULT;
1983 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001984
1985#if 0
Linus Torvalds1da177e2005-04-16 15:20:36 -07001986 if ((tmp.irq != bp->irq) ||
1987 (tmp.port != bp->base) ||
1988 (tmp.type != PORT_CIRRUS) ||
1989 (tmp.baud_base != (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC) ||
1990 (tmp.custom_divisor != 0) ||
1991 (tmp.xmit_fifo_size != CD186x_NFIFO) ||
1992 (tmp.flags & ~SPECIALIX_LEGAL_FLAGS)) {
1993 func_exit();
1994 return -EINVAL;
1995 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001996#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001997
1998 change_speed = ((port->flags & ASYNC_SPD_MASK) !=
1999 (tmp.flags & ASYNC_SPD_MASK));
2000 change_speed |= (tmp.custom_divisor != port->custom_divisor);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002001
Linus Torvalds1da177e2005-04-16 15:20:36 -07002002 if (!capable(CAP_SYS_ADMIN)) {
2003 if ((tmp.close_delay != port->close_delay) ||
2004 (tmp.closing_wait != port->closing_wait) ||
2005 ((tmp.flags & ~ASYNC_USR_MASK) !=
2006 (port->flags & ~ASYNC_USR_MASK))) {
2007 func_exit();
2008 return -EPERM;
2009 }
2010 port->flags = ((port->flags & ~ASYNC_USR_MASK) |
2011 (tmp.flags & ASYNC_USR_MASK));
2012 port->custom_divisor = tmp.custom_divisor;
2013 } else {
2014 port->flags = ((port->flags & ~ASYNC_FLAGS) |
2015 (tmp.flags & ASYNC_FLAGS));
2016 port->close_delay = tmp.close_delay;
2017 port->closing_wait = tmp.closing_wait;
2018 port->custom_divisor = tmp.custom_divisor;
2019 }
2020 if (change_speed) {
2021 sx_change_speed(bp, port);
2022 }
2023 func_exit();
2024 return 0;
2025}
2026
2027
2028static inline int sx_get_serial_info(struct specialix_port * port,
2029 struct serial_struct __user *retinfo)
2030{
2031 struct serial_struct tmp;
2032 struct specialix_board *bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002033
Linus Torvalds1da177e2005-04-16 15:20:36 -07002034 func_enter();
2035
2036 /*
Jesper Juhle49332b2005-05-01 08:59:08 -07002037 if (!access_ok(VERIFY_WRITE, (void *) retinfo, sizeof(tmp)))
2038 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002039 */
2040
2041 memset(&tmp, 0, sizeof(tmp));
2042 tmp.type = PORT_CIRRUS;
2043 tmp.line = port - sx_port;
2044 tmp.port = bp->base;
2045 tmp.irq = bp->irq;
2046 tmp.flags = port->flags;
2047 tmp.baud_base = (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC;
2048 tmp.close_delay = port->close_delay * HZ/100;
2049 tmp.closing_wait = port->closing_wait * HZ/100;
2050 tmp.custom_divisor = port->custom_divisor;
2051 tmp.xmit_fifo_size = CD186x_NFIFO;
2052 if (copy_to_user(retinfo, &tmp, sizeof(tmp))) {
2053 func_exit();
2054 return -EFAULT;
2055 }
2056
2057 func_exit();
2058 return 0;
2059}
2060
2061
Jeff Garzikd61780c2005-10-30 15:01:51 -08002062static int sx_ioctl(struct tty_struct * tty, struct file * filp,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002063 unsigned int cmd, unsigned long arg)
2064{
2065 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2066 int retval;
2067 void __user *argp = (void __user *)arg;
2068
2069 func_enter();
2070
2071 if (sx_paranoia_check(port, tty->name, "sx_ioctl")) {
2072 func_exit();
2073 return -ENODEV;
2074 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002075
Linus Torvalds1da177e2005-04-16 15:20:36 -07002076 switch (cmd) {
2077 case TCSBRK: /* SVID version: non-zero arg --> no break */
2078 retval = tty_check_change(tty);
2079 if (retval) {
2080 func_exit();
2081 return retval;
2082 }
2083 tty_wait_until_sent(tty, 0);
2084 if (!arg)
2085 sx_send_break(port, HZ/4); /* 1/4 second */
2086 return 0;
2087 case TCSBRKP: /* support for POSIX tcsendbreak() */
2088 retval = tty_check_change(tty);
2089 if (retval) {
2090 func_exit();
2091 return retval;
2092 }
2093 tty_wait_until_sent(tty, 0);
2094 sx_send_break(port, arg ? arg*(HZ/10) : HZ/4);
2095 func_exit();
2096 return 0;
2097 case TIOCGSOFTCAR:
2098 if (put_user(C_CLOCAL(tty)?1:0, (unsigned long __user *)argp)) {
2099 func_exit();
2100 return -EFAULT;
2101 }
2102 func_exit();
2103 return 0;
2104 case TIOCSSOFTCAR:
2105 if (get_user(arg, (unsigned long __user *) argp)) {
2106 func_exit();
2107 return -EFAULT;
2108 }
2109 tty->termios->c_cflag =
2110 ((tty->termios->c_cflag & ~CLOCAL) |
2111 (arg ? CLOCAL : 0));
2112 func_exit();
2113 return 0;
2114 case TIOCGSERIAL:
2115 func_exit();
2116 return sx_get_serial_info(port, argp);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002117 case TIOCSSERIAL:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002118 func_exit();
2119 return sx_set_serial_info(port, argp);
2120 default:
2121 func_exit();
2122 return -ENOIOCTLCMD;
2123 }
2124 func_exit();
2125 return 0;
2126}
2127
2128
2129static void sx_throttle(struct tty_struct * tty)
2130{
2131 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2132 struct specialix_board *bp;
2133 unsigned long flags;
2134
2135 func_enter();
2136
2137 if (sx_paranoia_check(port, tty->name, "sx_throttle")) {
2138 func_exit();
2139 return;
2140 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002141
Linus Torvalds1da177e2005-04-16 15:20:36 -07002142 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002143
Linus Torvalds1da177e2005-04-16 15:20:36 -07002144 /* Use DTR instead of RTS ! */
Jeff Garzikd61780c2005-10-30 15:01:51 -08002145 if (SX_CRTSCTS (tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002146 port->MSVR &= ~MSVR_DTR;
2147 else {
2148 /* Auch!!! I think the system shouldn't call this then. */
2149 /* Or maybe we're supposed (allowed?) to do our side of hw
Jeff Garzikd61780c2005-10-30 15:01:51 -08002150 handshake anyway, even when hardware handshake is off.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002151 When you see this in your logs, please report.... */
2152 printk (KERN_ERR "sx%d: Need to throttle, but can't (hardware hs is off)\n",
2153 port_No (port));
2154 }
2155 spin_lock_irqsave(&bp->lock, flags);
2156 sx_out(bp, CD186x_CAR, port_No(port));
2157 spin_unlock_irqrestore(&bp->lock, flags);
2158 if (I_IXOFF(tty)) {
2159 spin_unlock_irqrestore(&bp->lock, flags);
2160 sx_wait_CCR(bp);
2161 spin_lock_irqsave(&bp->lock, flags);
2162 sx_out(bp, CD186x_CCR, CCR_SSCH2);
2163 spin_unlock_irqrestore(&bp->lock, flags);
2164 sx_wait_CCR(bp);
2165 }
2166 spin_lock_irqsave(&bp->lock, flags);
2167 sx_out(bp, CD186x_MSVR, port->MSVR);
2168 spin_unlock_irqrestore(&bp->lock, flags);
2169
2170 func_exit();
2171}
2172
2173
2174static void sx_unthrottle(struct tty_struct * tty)
2175{
2176 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2177 struct specialix_board *bp;
2178 unsigned long flags;
2179
2180 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002181
Linus Torvalds1da177e2005-04-16 15:20:36 -07002182 if (sx_paranoia_check(port, tty->name, "sx_unthrottle")) {
2183 func_exit();
2184 return;
2185 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002186
Linus Torvalds1da177e2005-04-16 15:20:36 -07002187 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002188
Linus Torvalds1da177e2005-04-16 15:20:36 -07002189 spin_lock_irqsave(&port->lock, flags);
2190 /* XXXX Use DTR INSTEAD???? */
2191 if (SX_CRTSCTS(tty)) {
2192 port->MSVR |= MSVR_DTR;
2193 } /* Else clause: see remark in "sx_throttle"... */
2194 spin_lock_irqsave(&bp->lock, flags);
2195 sx_out(bp, CD186x_CAR, port_No(port));
2196 spin_unlock_irqrestore(&bp->lock, flags);
2197 if (I_IXOFF(tty)) {
2198 spin_unlock_irqrestore(&port->lock, flags);
2199 sx_wait_CCR(bp);
2200 spin_lock_irqsave(&bp->lock, flags);
2201 sx_out(bp, CD186x_CCR, CCR_SSCH1);
2202 spin_unlock_irqrestore(&bp->lock, flags);
2203 sx_wait_CCR(bp);
2204 spin_lock_irqsave(&port->lock, flags);
2205 }
2206 spin_lock_irqsave(&bp->lock, flags);
2207 sx_out(bp, CD186x_MSVR, port->MSVR);
2208 spin_unlock_irqrestore(&bp->lock, flags);
2209 spin_unlock_irqrestore(&port->lock, flags);
2210
2211 func_exit();
2212}
2213
2214
2215static void sx_stop(struct tty_struct * tty)
2216{
2217 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2218 struct specialix_board *bp;
2219 unsigned long flags;
2220
2221 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002222
Linus Torvalds1da177e2005-04-16 15:20:36 -07002223 if (sx_paranoia_check(port, tty->name, "sx_stop")) {
2224 func_exit();
2225 return;
2226 }
2227
2228 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002229
Linus Torvalds1da177e2005-04-16 15:20:36 -07002230 spin_lock_irqsave(&port->lock, flags);
2231 port->IER &= ~IER_TXRDY;
2232 spin_lock_irqsave(&bp->lock, flags);
2233 sx_out(bp, CD186x_CAR, port_No(port));
2234 sx_out(bp, CD186x_IER, port->IER);
2235 spin_unlock_irqrestore(&bp->lock, flags);
2236 spin_unlock_irqrestore(&port->lock, flags);
2237
2238 func_exit();
2239}
2240
2241
2242static void sx_start(struct tty_struct * tty)
2243{
2244 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2245 struct specialix_board *bp;
2246 unsigned long flags;
2247
2248 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002249
Linus Torvalds1da177e2005-04-16 15:20:36 -07002250 if (sx_paranoia_check(port, tty->name, "sx_start")) {
2251 func_exit();
2252 return;
2253 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002254
Linus Torvalds1da177e2005-04-16 15:20:36 -07002255 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002256
Linus Torvalds1da177e2005-04-16 15:20:36 -07002257 spin_lock_irqsave(&port->lock, flags);
2258 if (port->xmit_cnt && port->xmit_buf && !(port->IER & IER_TXRDY)) {
2259 port->IER |= IER_TXRDY;
2260 spin_lock_irqsave(&bp->lock, flags);
2261 sx_out(bp, CD186x_CAR, port_No(port));
2262 sx_out(bp, CD186x_IER, port->IER);
2263 spin_unlock_irqrestore(&bp->lock, flags);
2264 }
2265 spin_unlock_irqrestore(&port->lock, flags);
2266
2267 func_exit();
2268}
2269
2270
2271/*
2272 * This routine is called from the work-queue when the interrupt
2273 * routine has signalled that a hangup has occurred. The path of
2274 * hangup processing is:
2275 *
2276 * serial interrupt routine -> (workqueue) ->
2277 * do_sx_hangup() -> tty->hangup() -> sx_hangup()
Jeff Garzikd61780c2005-10-30 15:01:51 -08002278 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07002279 */
2280static void do_sx_hangup(void *private_)
2281{
2282 struct specialix_port *port = (struct specialix_port *) private_;
2283 struct tty_struct *tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002284
Linus Torvalds1da177e2005-04-16 15:20:36 -07002285 func_enter();
2286
2287 tty = port->tty;
2288 if (tty)
2289 tty_hangup(tty); /* FIXME: module removal race here */
2290
2291 func_exit();
2292}
2293
2294
2295static void sx_hangup(struct tty_struct * tty)
2296{
2297 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2298 struct specialix_board *bp;
2299 unsigned long flags;
2300
2301 func_enter();
2302
2303 if (sx_paranoia_check(port, tty->name, "sx_hangup")) {
2304 func_exit();
2305 return;
2306 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002307
Linus Torvalds1da177e2005-04-16 15:20:36 -07002308 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002309
Linus Torvalds1da177e2005-04-16 15:20:36 -07002310 sx_shutdown_port(bp, port);
2311 spin_lock_irqsave(&port->lock, flags);
2312 port->event = 0;
2313 bp->count -= port->count;
2314 if (bp->count < 0) {
2315 printk(KERN_ERR "sx%d: sx_hangup: bad board count: %d port: %d\n",
2316 board_No(bp), bp->count, tty->index);
2317 bp->count = 0;
2318 }
2319 port->count = 0;
2320 port->flags &= ~ASYNC_NORMAL_ACTIVE;
2321 port->tty = NULL;
2322 spin_unlock_irqrestore(&port->lock, flags);
2323 wake_up_interruptible(&port->open_wait);
2324
2325 func_exit();
2326}
2327
2328
2329static void sx_set_termios(struct tty_struct * tty, struct termios * old_termios)
2330{
2331 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2332 unsigned long flags;
2333 struct specialix_board * bp;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002334
Linus Torvalds1da177e2005-04-16 15:20:36 -07002335 if (sx_paranoia_check(port, tty->name, "sx_set_termios"))
2336 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002337
Linus Torvalds1da177e2005-04-16 15:20:36 -07002338 if (tty->termios->c_cflag == old_termios->c_cflag &&
2339 tty->termios->c_iflag == old_termios->c_iflag)
2340 return;
2341
2342 bp = port_Board(port);
2343 spin_lock_irqsave(&port->lock, flags);
2344 sx_change_speed(port_Board(port), port);
2345 spin_unlock_irqrestore(&port->lock, flags);
2346
2347 if ((old_termios->c_cflag & CRTSCTS) &&
2348 !(tty->termios->c_cflag & CRTSCTS)) {
2349 tty->hw_stopped = 0;
2350 sx_start(tty);
2351 }
2352}
2353
2354
2355static void do_softint(void *private_)
2356{
2357 struct specialix_port *port = (struct specialix_port *) private_;
2358 struct tty_struct *tty;
2359
2360 func_enter();
2361
2362 if(!(tty = port->tty)) {
2363 func_exit();
2364 return;
2365 }
2366
2367 if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) {
2368 tty_wakeup(tty);
2369 //wake_up_interruptible(&tty->write_wait);
2370 }
2371
2372 func_exit();
2373}
2374
2375static struct tty_operations sx_ops = {
2376 .open = sx_open,
2377 .close = sx_close,
2378 .write = sx_write,
2379 .put_char = sx_put_char,
2380 .flush_chars = sx_flush_chars,
2381 .write_room = sx_write_room,
2382 .chars_in_buffer = sx_chars_in_buffer,
2383 .flush_buffer = sx_flush_buffer,
2384 .ioctl = sx_ioctl,
2385 .throttle = sx_throttle,
2386 .unthrottle = sx_unthrottle,
2387 .set_termios = sx_set_termios,
2388 .stop = sx_stop,
2389 .start = sx_start,
2390 .hangup = sx_hangup,
2391 .tiocmget = sx_tiocmget,
2392 .tiocmset = sx_tiocmset,
2393};
2394
2395static int sx_init_drivers(void)
2396{
2397 int error;
2398 int i;
2399
2400 func_enter();
2401
2402 specialix_driver = alloc_tty_driver(SX_NBOARD * SX_NPORT);
2403 if (!specialix_driver) {
2404 printk(KERN_ERR "sx: Couldn't allocate tty_driver.\n");
2405 func_exit();
2406 return 1;
2407 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002408
Linus Torvalds1da177e2005-04-16 15:20:36 -07002409 if (!(tmp_buf = (unsigned char *) get_zeroed_page(GFP_KERNEL))) {
2410 printk(KERN_ERR "sx: Couldn't get free page.\n");
2411 put_tty_driver(specialix_driver);
2412 func_exit();
2413 return 1;
2414 }
2415 specialix_driver->owner = THIS_MODULE;
2416 specialix_driver->name = "ttyW";
2417 specialix_driver->major = SPECIALIX_NORMAL_MAJOR;
2418 specialix_driver->type = TTY_DRIVER_TYPE_SERIAL;
2419 specialix_driver->subtype = SERIAL_TYPE_NORMAL;
2420 specialix_driver->init_termios = tty_std_termios;
2421 specialix_driver->init_termios.c_cflag =
2422 B9600 | CS8 | CREAD | HUPCL | CLOCAL;
2423 specialix_driver->flags = TTY_DRIVER_REAL_RAW;
2424 tty_set_operations(specialix_driver, &sx_ops);
2425
2426 if ((error = tty_register_driver(specialix_driver))) {
2427 put_tty_driver(specialix_driver);
2428 free_page((unsigned long)tmp_buf);
2429 printk(KERN_ERR "sx: Couldn't register specialix IO8+ driver, error = %d\n",
2430 error);
2431 func_exit();
2432 return 1;
2433 }
2434 memset(sx_port, 0, sizeof(sx_port));
2435 for (i = 0; i < SX_NPORT * SX_NBOARD; i++) {
2436 sx_port[i].magic = SPECIALIX_MAGIC;
2437 INIT_WORK(&sx_port[i].tqueue, do_softint, &sx_port[i]);
2438 INIT_WORK(&sx_port[i].tqueue_hangup, do_sx_hangup, &sx_port[i]);
2439 sx_port[i].close_delay = 50 * HZ/100;
2440 sx_port[i].closing_wait = 3000 * HZ/100;
2441 init_waitqueue_head(&sx_port[i].open_wait);
2442 init_waitqueue_head(&sx_port[i].close_wait);
2443 spin_lock_init(&sx_port[i].lock);
2444 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002445
Linus Torvalds1da177e2005-04-16 15:20:36 -07002446 func_exit();
2447 return 0;
2448}
2449
2450static void sx_release_drivers(void)
2451{
2452 func_enter();
2453
2454 free_page((unsigned long)tmp_buf);
2455 tty_unregister_driver(specialix_driver);
2456 put_tty_driver(specialix_driver);
2457 func_exit();
2458}
2459
Jeff Garzikd61780c2005-10-30 15:01:51 -08002460/*
2461 * This routine must be called by kernel at boot time
Linus Torvalds1da177e2005-04-16 15:20:36 -07002462 */
2463static int __init specialix_init(void)
2464{
2465 int i;
2466 int found = 0;
2467
2468 func_enter();
2469
2470 printk(KERN_INFO "sx: Specialix IO8+ driver v" VERSION ", (c) R.E.Wolff 1997/1998.\n");
2471 printk(KERN_INFO "sx: derived from work (c) D.Gorodchanin 1994-1996.\n");
2472#ifdef CONFIG_SPECIALIX_RTSCTS
2473 printk (KERN_INFO "sx: DTR/RTS pin is always RTS.\n");
2474#else
2475 printk (KERN_INFO "sx: DTR/RTS pin is RTS when CRTSCTS is on.\n");
2476#endif
Jeff Garzikd61780c2005-10-30 15:01:51 -08002477
Linus Torvalds1da177e2005-04-16 15:20:36 -07002478 for (i = 0; i < SX_NBOARD; i++)
Ingo Molnar34af9462006-06-27 02:53:55 -07002479 spin_lock_init(&sx_board[i].lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002480
2481 if (sx_init_drivers()) {
2482 func_exit();
2483 return -EIO;
2484 }
2485
Jeff Garzikd61780c2005-10-30 15:01:51 -08002486 for (i = 0; i < SX_NBOARD; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002487 if (sx_board[i].base && !sx_probe(&sx_board[i]))
2488 found++;
2489
2490#ifdef CONFIG_PCI
2491 {
2492 struct pci_dev *pdev = NULL;
2493
2494 i=0;
2495 while (i < SX_NBOARD) {
2496 if (sx_board[i].flags & SX_BOARD_PRESENT) {
2497 i++;
2498 continue;
2499 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002500 pdev = pci_find_device (PCI_VENDOR_ID_SPECIALIX,
2501 PCI_DEVICE_ID_SPECIALIX_IO8,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002502 pdev);
2503 if (!pdev) break;
2504
2505 if (pci_enable_device(pdev))
2506 continue;
2507
2508 sx_board[i].irq = pdev->irq;
2509
2510 sx_board[i].base = pci_resource_start (pdev, 2);
2511
2512 sx_board[i].flags |= SX_BOARD_IS_PCI;
2513 if (!sx_probe(&sx_board[i]))
2514 found ++;
2515 }
2516 }
2517#endif
2518
2519 if (!found) {
2520 sx_release_drivers();
2521 printk(KERN_INFO "sx: No specialix IO8+ boards detected.\n");
2522 func_exit();
2523 return -EIO;
2524 }
2525
2526 func_exit();
2527 return 0;
2528}
2529
2530static int iobase[SX_NBOARD] = {0,};
2531
2532static int irq [SX_NBOARD] = {0,};
2533
2534module_param_array(iobase, int, NULL, 0);
2535module_param_array(irq, int, NULL, 0);
2536module_param(sx_debug, int, 0);
2537module_param(sx_rxfifo, int, 0);
2538#ifdef SPECIALIX_TIMER
2539module_param(sx_poll, int, 0);
2540#endif
2541
2542/*
2543 * You can setup up to 4 boards.
2544 * by specifying "iobase=0xXXX,0xXXX ..." as insmod parameter.
Jeff Garzikd61780c2005-10-30 15:01:51 -08002545 * You should specify the IRQs too in that case "irq=....,...".
2546 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07002547 * More than 4 boards in one computer is not possible, as the card can
Jeff Garzikd61780c2005-10-30 15:01:51 -08002548 * only use 4 different interrupts.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002549 *
2550 */
2551static int __init specialix_init_module(void)
2552{
2553 int i;
2554
2555 func_enter();
2556
Linus Torvalds1da177e2005-04-16 15:20:36 -07002557 if (iobase[0] || iobase[1] || iobase[2] || iobase[3]) {
2558 for(i = 0; i < SX_NBOARD; i++) {
2559 sx_board[i].base = iobase[i];
2560 sx_board[i].irq = irq[i];
2561 sx_board[i].count= 0;
2562 }
2563 }
2564
2565 func_exit();
2566
2567 return specialix_init();
2568}
Jeff Garzikd61780c2005-10-30 15:01:51 -08002569
Linus Torvalds1da177e2005-04-16 15:20:36 -07002570static void __exit specialix_exit_module(void)
2571{
2572 int i;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002573
Linus Torvalds1da177e2005-04-16 15:20:36 -07002574 func_enter();
2575
2576 sx_release_drivers();
2577 for (i = 0; i < SX_NBOARD; i++)
Jeff Garzikd61780c2005-10-30 15:01:51 -08002578 if (sx_board[i].flags & SX_BOARD_PRESENT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002579 sx_release_io_range(&sx_board[i]);
2580#ifdef SPECIALIX_TIMER
2581 del_timer (&missed_irq_timer);
2582#endif
2583
2584 func_exit();
2585}
2586
2587module_init(specialix_init_module);
2588module_exit(specialix_exit_module);
2589
2590MODULE_LICENSE("GPL");