blob: c0ef0f0e5800dcd74a90e38cc483bc2e37ac60f0 [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);
Alan Cox67cc0162006-09-29 02:01:39 -07001090 baud = tty_get_baud_rate(tty);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001091
Alan Cox67cc0162006-09-29 02:01:39 -07001092 if (baud == 38400) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093 if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
1094 baud ++;
1095 if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
1096 baud += 2;
1097 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001098
Alan Cox67cc0162006-09-29 02:01:39 -07001099 if (!baud) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001100 /* Drop DTR & exit */
1101 dprintk (SX_DEBUG_TERMIOS, "Dropping DTR... Hmm....\n");
1102 if (!SX_CRTSCTS (tty)) {
1103 port -> MSVR &= ~ MSVR_DTR;
1104 spin_lock_irqsave(&bp->lock, flags);
1105 sx_out(bp, CD186x_MSVR, port->MSVR );
1106 spin_unlock_irqrestore(&bp->lock, flags);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001107 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108 else
1109 dprintk (SX_DEBUG_TERMIOS, "Can't drop DTR: no DTR.\n");
1110 return;
1111 } else {
1112 /* Set DTR on */
1113 if (!SX_CRTSCTS (tty)) {
1114 port ->MSVR |= MSVR_DTR;
1115 }
1116 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001117
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118 /*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001119 * Now we must calculate some speed depended things
Linus Torvalds1da177e2005-04-16 15:20:36 -07001120 */
1121
1122 /* Set baud rate for port */
1123 tmp = port->custom_divisor ;
1124 if ( tmp )
1125 printk (KERN_INFO "sx%d: Using custom baud rate divisor %ld. \n"
1126 "This is an untested option, please be carefull.\n",
1127 port_No (port), tmp);
1128 else
Alan Cox67cc0162006-09-29 02:01:39 -07001129 tmp = (((SX_OSCFREQ + baud/2) / baud +
Linus Torvalds1da177e2005-04-16 15:20:36 -07001130 CD186x_TPC/2) / CD186x_TPC);
1131
Jeff Garzikd61780c2005-10-30 15:01:51 -08001132 if ((tmp < 0x10) && time_before(again, jiffies)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133 again = jiffies + HZ * 60;
1134 /* Page 48 of version 2.0 of the CL-CD1865 databook */
1135 if (tmp >= 12) {
1136 printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
1137 "Performance degradation is possible.\n"
1138 "Read specialix.txt for more info.\n",
1139 port_No (port), tmp);
1140 } else {
1141 printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
1142 "Warning: overstressing Cirrus chip. "
1143 "This might not work.\n"
Jeff Garzikd61780c2005-10-30 15:01:51 -08001144 "Read specialix.txt for more info.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001145 port_No (port), tmp);
1146 }
1147 }
1148 spin_lock_irqsave(&bp->lock, flags);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001149 sx_out(bp, CD186x_RBPRH, (tmp >> 8) & 0xff);
1150 sx_out(bp, CD186x_TBPRH, (tmp >> 8) & 0xff);
1151 sx_out(bp, CD186x_RBPRL, tmp & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152 sx_out(bp, CD186x_TBPRL, tmp & 0xff);
1153 spin_unlock_irqrestore(&bp->lock, flags);
1154 if (port->custom_divisor) {
1155 baud = (SX_OSCFREQ + port->custom_divisor/2) / port->custom_divisor;
1156 baud = ( baud + 5 ) / 10;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001157 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158 baud = (baud_table[baud] + 5) / 10; /* Estimated CPS */
1159
1160 /* Two timer ticks seems enough to wakeup something like SLIP driver */
Jeff Garzikd61780c2005-10-30 15:01:51 -08001161 tmp = ((baud + HZ/2) / HZ) * 2 - CD186x_NFIFO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ?
1163 SERIAL_XMIT_SIZE - 1 : tmp);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001164
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165 /* Receiver timeout will be transmission time for 1.5 chars */
1166 tmp = (SPECIALIX_TPS + SPECIALIX_TPS/2 + baud/2) / baud;
1167 tmp = (tmp > 0xff) ? 0xff : tmp;
1168 spin_lock_irqsave(&bp->lock, flags);
1169 sx_out(bp, CD186x_RTPR, tmp);
1170 spin_unlock_irqrestore(&bp->lock, flags);
1171 switch (C_CSIZE(tty)) {
1172 case CS5:
1173 cor1 |= COR1_5BITS;
1174 break;
1175 case CS6:
1176 cor1 |= COR1_6BITS;
1177 break;
1178 case CS7:
1179 cor1 |= COR1_7BITS;
1180 break;
1181 case CS8:
1182 cor1 |= COR1_8BITS;
1183 break;
1184 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001185
1186 if (C_CSTOPB(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187 cor1 |= COR1_2SB;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001188
Linus Torvalds1da177e2005-04-16 15:20:36 -07001189 cor1 |= COR1_IGNORE;
1190 if (C_PARENB(tty)) {
1191 cor1 |= COR1_NORMPAR;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001192 if (C_PARODD(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001193 cor1 |= COR1_ODDP;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001194 if (I_INPCK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195 cor1 &= ~COR1_IGNORE;
1196 }
1197 /* Set marking of some errors */
1198 port->mark_mask = RCSR_OE | RCSR_TOUT;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001199 if (I_INPCK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001200 port->mark_mask |= RCSR_FE | RCSR_PE;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001201 if (I_BRKINT(tty) || I_PARMRK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202 port->mark_mask |= RCSR_BREAK;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001203 if (I_IGNPAR(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204 port->mark_mask &= ~(RCSR_FE | RCSR_PE);
1205 if (I_IGNBRK(tty)) {
1206 port->mark_mask &= ~RCSR_BREAK;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001207 if (I_IGNPAR(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208 /* Real raw mode. Ignore all */
1209 port->mark_mask &= ~RCSR_OE;
1210 }
1211 /* Enable Hardware Flow Control */
1212 if (C_CRTSCTS(tty)) {
1213#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
1214 port->IER |= IER_DSR | IER_CTS;
1215 mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD;
1216 mcor2 |= MCOR2_DSROD | MCOR2_CTSOD;
1217 spin_lock_irqsave(&bp->lock, flags);
1218 tty->hw_stopped = !(sx_in(bp, CD186x_MSVR) & (MSVR_CTS|MSVR_DSR));
1219 spin_unlock_irqrestore(&bp->lock, flags);
1220#else
Jeff Garzikd61780c2005-10-30 15:01:51 -08001221 port->COR2 |= COR2_CTSAE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001222#endif
1223 }
1224 /* Enable Software Flow Control. FIXME: I'm not sure about this */
1225 /* Some people reported that it works, but I still doubt it */
1226 if (I_IXON(tty)) {
1227 port->COR2 |= COR2_TXIBE;
1228 cor3 |= (COR3_FCT | COR3_SCDE);
1229 if (I_IXANY(tty))
1230 port->COR2 |= COR2_IXM;
1231 spin_lock_irqsave(&bp->lock, flags);
1232 sx_out(bp, CD186x_SCHR1, START_CHAR(tty));
1233 sx_out(bp, CD186x_SCHR2, STOP_CHAR(tty));
1234 sx_out(bp, CD186x_SCHR3, START_CHAR(tty));
1235 sx_out(bp, CD186x_SCHR4, STOP_CHAR(tty));
1236 spin_unlock_irqrestore(&bp->lock, flags);
1237 }
1238 if (!C_CLOCAL(tty)) {
1239 /* Enable CD check */
1240 port->IER |= IER_CD;
1241 mcor1 |= MCOR1_CDZD;
1242 mcor2 |= MCOR2_CDOD;
1243 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001244
1245 if (C_CREAD(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001246 /* Enable receiver */
1247 port->IER |= IER_RXD;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001248
Linus Torvalds1da177e2005-04-16 15:20:36 -07001249 /* Set input FIFO size (1-8 bytes) */
1250 cor3 |= sx_rxfifo;
1251 /* Setting up CD186x channel registers */
1252 spin_lock_irqsave(&bp->lock, flags);
1253 sx_out(bp, CD186x_COR1, cor1);
1254 sx_out(bp, CD186x_COR2, port->COR2);
1255 sx_out(bp, CD186x_COR3, cor3);
1256 spin_unlock_irqrestore(&bp->lock, flags);
1257 /* Make CD186x know about registers change */
1258 sx_wait_CCR(bp);
1259 spin_lock_irqsave(&bp->lock, flags);
1260 sx_out(bp, CD186x_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3);
1261 /* Setting up modem option registers */
1262 dprintk (SX_DEBUG_TERMIOS, "Mcor1 = %02x, mcor2 = %02x.\n", mcor1, mcor2);
1263 sx_out(bp, CD186x_MCOR1, mcor1);
1264 sx_out(bp, CD186x_MCOR2, mcor2);
1265 spin_unlock_irqrestore(&bp->lock, flags);
1266 /* Enable CD186x transmitter & receiver */
1267 sx_wait_CCR(bp);
1268 spin_lock_irqsave(&bp->lock, flags);
1269 sx_out(bp, CD186x_CCR, CCR_TXEN | CCR_RXEN);
1270 /* Enable interrupts */
1271 sx_out(bp, CD186x_IER, port->IER);
1272 /* And finally set the modem lines... */
1273 sx_out(bp, CD186x_MSVR, port->MSVR);
1274 spin_unlock_irqrestore(&bp->lock, flags);
1275
1276 func_exit();
1277}
1278
1279
1280/* Must be called with interrupts enabled */
1281static int sx_setup_port(struct specialix_board *bp, struct specialix_port *port)
1282{
1283 unsigned long flags;
1284
1285 func_enter();
1286
1287 if (port->flags & ASYNC_INITIALIZED) {
1288 func_exit();
1289 return 0;
1290 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001291
Linus Torvalds1da177e2005-04-16 15:20:36 -07001292 if (!port->xmit_buf) {
1293 /* We may sleep in get_zeroed_page() */
1294 unsigned long tmp;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001295
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296 if (!(tmp = get_zeroed_page(GFP_KERNEL))) {
1297 func_exit();
1298 return -ENOMEM;
1299 }
1300
1301 if (port->xmit_buf) {
1302 free_page(tmp);
1303 func_exit();
1304 return -ERESTARTSYS;
1305 }
1306 port->xmit_buf = (unsigned char *) tmp;
1307 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001308
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309 spin_lock_irqsave(&port->lock, flags);
1310
Jeff Garzikd61780c2005-10-30 15:01:51 -08001311 if (port->tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001312 clear_bit(TTY_IO_ERROR, &port->tty->flags);
1313
1314 port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
1315 sx_change_speed(bp, port);
1316 port->flags |= ASYNC_INITIALIZED;
1317
1318 spin_unlock_irqrestore(&port->lock, flags);
1319
Jeff Garzikd61780c2005-10-30 15:01:51 -08001320
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321 func_exit();
1322 return 0;
1323}
1324
1325
1326/* Must be called with interrupts disabled */
1327static void sx_shutdown_port(struct specialix_board *bp, struct specialix_port *port)
1328{
1329 struct tty_struct *tty;
1330 int i;
1331 unsigned long flags;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001332
Linus Torvalds1da177e2005-04-16 15:20:36 -07001333 func_enter();
1334
1335 if (!(port->flags & ASYNC_INITIALIZED)) {
1336 func_exit();
1337 return;
1338 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001339
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340 if (sx_debug & SX_DEBUG_FIFO) {
1341 dprintk(SX_DEBUG_FIFO, "sx%d: port %d: %ld overruns, FIFO hits [ ",
1342 board_No(bp), port_No(port), port->overrun);
1343 for (i = 0; i < 10; i++) {
1344 dprintk(SX_DEBUG_FIFO, "%ld ", port->hits[i]);
1345 }
1346 dprintk(SX_DEBUG_FIFO, "].\n");
1347 }
1348
1349 if (port->xmit_buf) {
1350 free_page((unsigned long) port->xmit_buf);
1351 port->xmit_buf = NULL;
1352 }
1353
1354 /* Select port */
1355 spin_lock_irqsave(&bp->lock, flags);
1356 sx_out(bp, CD186x_CAR, port_No(port));
1357
1358 if (!(tty = port->tty) || C_HUPCL(tty)) {
1359 /* Drop DTR */
1360 sx_out(bp, CD186x_MSVDTR, 0);
1361 }
1362 spin_unlock_irqrestore(&bp->lock, flags);
1363 /* Reset port */
1364 sx_wait_CCR(bp);
1365 spin_lock_irqsave(&bp->lock, flags);
1366 sx_out(bp, CD186x_CCR, CCR_SOFTRESET);
1367 /* Disable all interrupts from this port */
1368 port->IER = 0;
1369 sx_out(bp, CD186x_IER, port->IER);
1370 spin_unlock_irqrestore(&bp->lock, flags);
1371 if (tty)
1372 set_bit(TTY_IO_ERROR, &tty->flags);
1373 port->flags &= ~ASYNC_INITIALIZED;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001374
1375 if (!bp->count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376 sx_shutdown_board(bp);
1377 func_exit();
1378}
1379
Jeff Garzikd61780c2005-10-30 15:01:51 -08001380
Linus Torvalds1da177e2005-04-16 15:20:36 -07001381static int block_til_ready(struct tty_struct *tty, struct file * filp,
1382 struct specialix_port *port)
1383{
1384 DECLARE_WAITQUEUE(wait, current);
1385 struct specialix_board *bp = port_Board(port);
1386 int retval;
1387 int do_clocal = 0;
1388 int CD;
1389 unsigned long flags;
1390
1391 func_enter();
1392
1393 /*
1394 * If the device is in the middle of being closed, then block
1395 * until it's done, and then try again.
1396 */
1397 if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {
1398 interruptible_sleep_on(&port->close_wait);
1399 if (port->flags & ASYNC_HUP_NOTIFY) {
1400 func_exit();
1401 return -EAGAIN;
1402 } else {
1403 func_exit();
1404 return -ERESTARTSYS;
1405 }
1406 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001407
Linus Torvalds1da177e2005-04-16 15:20:36 -07001408 /*
1409 * If non-blocking mode is set, or the port is not enabled,
1410 * then make the check up front and then exit.
1411 */
1412 if ((filp->f_flags & O_NONBLOCK) ||
1413 (tty->flags & (1 << TTY_IO_ERROR))) {
1414 port->flags |= ASYNC_NORMAL_ACTIVE;
1415 func_exit();
1416 return 0;
1417 }
1418
1419 if (C_CLOCAL(tty))
1420 do_clocal = 1;
1421
1422 /*
1423 * Block waiting for the carrier detect and the line to become
1424 * free (i.e., not in use by the callout). While we are in
1425 * this loop, info->count is dropped by one, so that
1426 * rs_close() knows when to free things. We restore it upon
1427 * exit, either normal or abnormal.
1428 */
1429 retval = 0;
1430 add_wait_queue(&port->open_wait, &wait);
1431 spin_lock_irqsave(&port->lock, flags);
1432 if (!tty_hung_up_p(filp)) {
1433 port->count--;
1434 }
1435 spin_unlock_irqrestore(&port->lock, flags);
1436 port->blocked_open++;
1437 while (1) {
1438 spin_lock_irqsave(&bp->lock, flags);
1439 sx_out(bp, CD186x_CAR, port_No(port));
1440 CD = sx_in(bp, CD186x_MSVR) & MSVR_CD;
1441 if (SX_CRTSCTS (tty)) {
1442 /* Activate RTS */
1443 port->MSVR |= MSVR_DTR; /* WTF? */
1444 sx_out (bp, CD186x_MSVR, port->MSVR);
1445 } else {
1446 /* Activate DTR */
1447 port->MSVR |= MSVR_DTR;
1448 sx_out (bp, CD186x_MSVR, port->MSVR);
1449 }
1450 spin_unlock_irqrestore(&bp->lock, flags);
1451 set_current_state(TASK_INTERRUPTIBLE);
1452 if (tty_hung_up_p(filp) ||
1453 !(port->flags & ASYNC_INITIALIZED)) {
1454 if (port->flags & ASYNC_HUP_NOTIFY)
1455 retval = -EAGAIN;
1456 else
Jeff Garzikd61780c2005-10-30 15:01:51 -08001457 retval = -ERESTARTSYS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001458 break;
1459 }
1460 if (!(port->flags & ASYNC_CLOSING) &&
1461 (do_clocal || CD))
1462 break;
1463 if (signal_pending(current)) {
1464 retval = -ERESTARTSYS;
1465 break;
1466 }
1467 schedule();
1468 }
1469
1470 set_current_state(TASK_RUNNING);
1471 remove_wait_queue(&port->open_wait, &wait);
1472 spin_lock_irqsave(&port->lock, flags);
1473 if (!tty_hung_up_p(filp)) {
1474 port->count++;
1475 }
1476 port->blocked_open--;
1477 spin_unlock_irqrestore(&port->lock, flags);
1478 if (retval) {
1479 func_exit();
1480 return retval;
1481 }
1482
1483 port->flags |= ASYNC_NORMAL_ACTIVE;
1484 func_exit();
1485 return 0;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001486}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001487
1488
1489static int sx_open(struct tty_struct * tty, struct file * filp)
1490{
1491 int board;
1492 int error;
1493 struct specialix_port * port;
1494 struct specialix_board * bp;
1495 int i;
1496 unsigned long flags;
1497
1498 func_enter();
1499
1500 board = SX_BOARD(tty->index);
1501
1502 if (board >= SX_NBOARD || !(sx_board[board].flags & SX_BOARD_PRESENT)) {
1503 func_exit();
1504 return -ENODEV;
1505 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001506
Linus Torvalds1da177e2005-04-16 15:20:36 -07001507 bp = &sx_board[board];
1508 port = sx_port + board * SX_NPORT + SX_PORT(tty->index);
1509 port->overrun = 0;
1510 for (i = 0; i < 10; i++)
1511 port->hits[i]=0;
1512
1513 dprintk (SX_DEBUG_OPEN, "Board = %d, bp = %p, port = %p, portno = %d.\n",
1514 board, bp, port, SX_PORT(tty->index));
1515
1516 if (sx_paranoia_check(port, tty->name, "sx_open")) {
1517 func_enter();
1518 return -ENODEV;
1519 }
1520
1521 if ((error = sx_setup_board(bp))) {
1522 func_exit();
1523 return error;
1524 }
1525
1526 spin_lock_irqsave(&bp->lock, flags);
1527 port->count++;
1528 bp->count++;
1529 tty->driver_data = port;
1530 port->tty = tty;
1531 spin_unlock_irqrestore(&bp->lock, flags);
1532
1533 if ((error = sx_setup_port(bp, port))) {
1534 func_enter();
1535 return error;
1536 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001537
Linus Torvalds1da177e2005-04-16 15:20:36 -07001538 if ((error = block_til_ready(tty, filp, port))) {
1539 func_enter();
1540 return error;
1541 }
1542
1543 func_exit();
1544 return 0;
1545}
1546
1547
1548static void sx_close(struct tty_struct * tty, struct file * filp)
1549{
1550 struct specialix_port *port = (struct specialix_port *) tty->driver_data;
1551 struct specialix_board *bp;
1552 unsigned long flags;
1553 unsigned long timeout;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001554
Linus Torvalds1da177e2005-04-16 15:20:36 -07001555 func_enter();
1556 if (!port || sx_paranoia_check(port, tty->name, "close")) {
1557 func_exit();
1558 return;
1559 }
1560 spin_lock_irqsave(&port->lock, flags);
1561
1562 if (tty_hung_up_p(filp)) {
1563 spin_unlock_irqrestore(&port->lock, flags);
1564 func_exit();
1565 return;
1566 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001567
Linus Torvalds1da177e2005-04-16 15:20:36 -07001568 bp = port_Board(port);
1569 if ((tty->count == 1) && (port->count != 1)) {
1570 printk(KERN_ERR "sx%d: sx_close: bad port count;"
1571 " tty->count is 1, port count is %d\n",
1572 board_No(bp), port->count);
1573 port->count = 1;
1574 }
1575
1576 if (port->count > 1) {
1577 port->count--;
1578 bp->count--;
1579
1580 spin_unlock_irqrestore(&port->lock, flags);
1581
1582 func_exit();
1583 return;
1584 }
1585 port->flags |= ASYNC_CLOSING;
1586 /*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001587 * Now we wait for the transmit buffer to clear; and we notify
Linus Torvalds1da177e2005-04-16 15:20:36 -07001588 * the line discipline to only process XON/XOFF characters.
1589 */
1590 tty->closing = 1;
1591 spin_unlock_irqrestore(&port->lock, flags);
1592 dprintk (SX_DEBUG_OPEN, "Closing\n");
1593 if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) {
1594 tty_wait_until_sent(tty, port->closing_wait);
1595 }
1596 /*
1597 * At this point we stop accepting input. To do this, we
1598 * disable the receive line status interrupts, and tell the
1599 * interrupt driver to stop checking the data ready bit in the
1600 * line status register.
1601 */
1602 dprintk (SX_DEBUG_OPEN, "Closed\n");
1603 port->IER &= ~IER_RXD;
1604 if (port->flags & ASYNC_INITIALIZED) {
1605 port->IER &= ~IER_TXRDY;
1606 port->IER |= IER_TXEMPTY;
1607 spin_lock_irqsave(&bp->lock, flags);
1608 sx_out(bp, CD186x_CAR, port_No(port));
1609 sx_out(bp, CD186x_IER, port->IER);
1610 spin_unlock_irqrestore(&bp->lock, flags);
1611 /*
1612 * Before we drop DTR, make sure the UART transmitter
1613 * has completely drained; this is especially
1614 * important if there is a transmit FIFO!
1615 */
1616 timeout = jiffies+HZ;
1617 while(port->IER & IER_TXEMPTY) {
1618 set_current_state (TASK_INTERRUPTIBLE);
1619 msleep_interruptible(jiffies_to_msecs(port->timeout));
1620 if (time_after(jiffies, timeout)) {
1621 printk (KERN_INFO "Timeout waiting for close\n");
1622 break;
1623 }
1624 }
1625
1626 }
1627
1628 if (--bp->count < 0) {
1629 printk(KERN_ERR "sx%d: sx_shutdown_port: bad board count: %d port: %d\n",
1630 board_No(bp), bp->count, tty->index);
1631 bp->count = 0;
1632 }
1633 if (--port->count < 0) {
1634 printk(KERN_ERR "sx%d: sx_close: bad port count for tty%d: %d\n",
1635 board_No(bp), port_No(port), port->count);
1636 port->count = 0;
1637 }
1638
1639 sx_shutdown_port(bp, port);
1640 if (tty->driver->flush_buffer)
1641 tty->driver->flush_buffer(tty);
1642 tty_ldisc_flush(tty);
1643 spin_lock_irqsave(&port->lock, flags);
1644 tty->closing = 0;
1645 port->event = 0;
1646 port->tty = NULL;
1647 spin_unlock_irqrestore(&port->lock, flags);
1648 if (port->blocked_open) {
1649 if (port->close_delay) {
1650 msleep_interruptible(jiffies_to_msecs(port->close_delay));
1651 }
1652 wake_up_interruptible(&port->open_wait);
1653 }
1654 port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
1655 wake_up_interruptible(&port->close_wait);
1656
1657 func_exit();
1658}
1659
1660
Jeff Garzikd61780c2005-10-30 15:01:51 -08001661static int sx_write(struct tty_struct * tty,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001662 const unsigned char *buf, int count)
1663{
1664 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1665 struct specialix_board *bp;
1666 int c, total = 0;
1667 unsigned long flags;
1668
1669 func_enter();
1670 if (sx_paranoia_check(port, tty->name, "sx_write")) {
1671 func_exit();
1672 return 0;
1673 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001674
Linus Torvalds1da177e2005-04-16 15:20:36 -07001675 bp = port_Board(port);
1676
Eric Sesterhenn326f28e92006-06-25 05:48:48 -07001677 if (!port->xmit_buf || !tmp_buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001678 func_exit();
1679 return 0;
1680 }
1681
1682 while (1) {
1683 spin_lock_irqsave(&port->lock, flags);
1684 c = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
1685 SERIAL_XMIT_SIZE - port->xmit_head));
1686 if (c <= 0) {
1687 spin_unlock_irqrestore(&port->lock, flags);
1688 break;
1689 }
1690 memcpy(port->xmit_buf + port->xmit_head, buf, c);
1691 port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
1692 port->xmit_cnt += c;
1693 spin_unlock_irqrestore(&port->lock, flags);
1694
1695 buf += c;
1696 count -= c;
1697 total += c;
1698 }
1699
1700 spin_lock_irqsave(&bp->lock, flags);
1701 if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
1702 !(port->IER & IER_TXRDY)) {
1703 port->IER |= IER_TXRDY;
1704 sx_out(bp, CD186x_CAR, port_No(port));
1705 sx_out(bp, CD186x_IER, port->IER);
1706 }
1707 spin_unlock_irqrestore(&bp->lock, flags);
1708 func_exit();
1709
1710 return total;
1711}
1712
1713
1714static void sx_put_char(struct tty_struct * tty, unsigned char ch)
1715{
1716 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1717 unsigned long flags;
1718 struct specialix_board * bp;
1719
1720 func_enter();
1721
1722 if (sx_paranoia_check(port, tty->name, "sx_put_char")) {
1723 func_exit();
1724 return;
1725 }
1726 dprintk (SX_DEBUG_TX, "check tty: %p %p\n", tty, port->xmit_buf);
Eric Sesterhenn326f28e92006-06-25 05:48:48 -07001727 if (!port->xmit_buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001728 func_exit();
1729 return;
1730 }
1731 bp = port_Board(port);
1732 spin_lock_irqsave(&port->lock, flags);
1733
1734 dprintk (SX_DEBUG_TX, "xmit_cnt: %d xmit_buf: %p\n", port->xmit_cnt, port->xmit_buf);
1735 if ((port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) || (!port->xmit_buf)) {
1736 spin_unlock_irqrestore(&port->lock, flags);
1737 dprintk (SX_DEBUG_TX, "Exit size\n");
1738 func_exit();
1739 return;
1740 }
1741 dprintk (SX_DEBUG_TX, "Handle xmit: %p %p\n", port, port->xmit_buf);
1742 port->xmit_buf[port->xmit_head++] = ch;
1743 port->xmit_head &= SERIAL_XMIT_SIZE - 1;
1744 port->xmit_cnt++;
1745 spin_unlock_irqrestore(&port->lock, flags);
1746
1747 func_exit();
1748}
1749
1750
1751static void sx_flush_chars(struct tty_struct * tty)
1752{
1753 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1754 unsigned long flags;
1755 struct specialix_board * bp = port_Board(port);
1756
1757 func_enter();
1758
1759 if (sx_paranoia_check(port, tty->name, "sx_flush_chars")) {
1760 func_exit();
1761 return;
1762 }
1763 if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
1764 !port->xmit_buf) {
1765 func_exit();
1766 return;
1767 }
1768 spin_lock_irqsave(&bp->lock, flags);
1769 port->IER |= IER_TXRDY;
1770 sx_out(port_Board(port), CD186x_CAR, port_No(port));
1771 sx_out(port_Board(port), CD186x_IER, port->IER);
1772 spin_unlock_irqrestore(&bp->lock, flags);
1773
1774 func_exit();
1775}
1776
1777
1778static int sx_write_room(struct tty_struct * tty)
1779{
1780 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1781 int ret;
1782
1783 func_enter();
1784
1785 if (sx_paranoia_check(port, tty->name, "sx_write_room")) {
1786 func_exit();
1787 return 0;
1788 }
1789
1790 ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;
1791 if (ret < 0)
1792 ret = 0;
1793
1794 func_exit();
1795 return ret;
1796}
1797
1798
1799static int sx_chars_in_buffer(struct tty_struct *tty)
1800{
1801 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1802
1803 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08001804
Linus Torvalds1da177e2005-04-16 15:20:36 -07001805 if (sx_paranoia_check(port, tty->name, "sx_chars_in_buffer")) {
1806 func_exit();
1807 return 0;
1808 }
1809 func_exit();
1810 return port->xmit_cnt;
1811}
1812
1813
1814static void sx_flush_buffer(struct tty_struct *tty)
1815{
1816 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1817 unsigned long flags;
1818 struct specialix_board * bp;
1819
1820 func_enter();
1821
1822 if (sx_paranoia_check(port, tty->name, "sx_flush_buffer")) {
1823 func_exit();
1824 return;
1825 }
1826
1827 bp = port_Board(port);
1828 spin_lock_irqsave(&port->lock, flags);
1829 port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
1830 spin_unlock_irqrestore(&port->lock, flags);
1831 tty_wakeup(tty);
1832
1833 func_exit();
1834}
1835
1836
1837static int sx_tiocmget(struct tty_struct *tty, struct file *file)
1838{
1839 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1840 struct specialix_board * bp;
1841 unsigned char status;
1842 unsigned int result;
1843 unsigned long flags;
1844
1845 func_enter();
1846
1847 if (sx_paranoia_check(port, tty->name, __FUNCTION__)) {
1848 func_exit();
1849 return -ENODEV;
1850 }
1851
1852 bp = port_Board(port);
1853 spin_lock_irqsave (&bp->lock, flags);
1854 sx_out(bp, CD186x_CAR, port_No(port));
1855 status = sx_in(bp, CD186x_MSVR);
1856 spin_unlock_irqrestore(&bp->lock, flags);
1857 dprintk (SX_DEBUG_INIT, "Got msvr[%d] = %02x, car = %d.\n",
1858 port_No(port), status, sx_in (bp, CD186x_CAR));
1859 dprintk (SX_DEBUG_INIT, "sx_port = %p, port = %p\n", sx_port, port);
1860 if (SX_CRTSCTS(port->tty)) {
Jeff Garzikd61780c2005-10-30 15:01:51 -08001861 result = /* (status & MSVR_RTS) ? */ TIOCM_DTR /* : 0) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001862 | ((status & MSVR_DTR) ? TIOCM_RTS : 0)
1863 | ((status & MSVR_CD) ? TIOCM_CAR : 0)
1864 |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */
1865 | ((status & MSVR_CTS) ? TIOCM_CTS : 0);
1866 } else {
Jeff Garzikd61780c2005-10-30 15:01:51 -08001867 result = /* (status & MSVR_RTS) ? */ TIOCM_RTS /* : 0) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001868 | ((status & MSVR_DTR) ? TIOCM_DTR : 0)
1869 | ((status & MSVR_CD) ? TIOCM_CAR : 0)
1870 |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */
1871 | ((status & MSVR_CTS) ? TIOCM_CTS : 0);
1872 }
1873
1874 func_exit();
1875
1876 return result;
1877}
1878
1879
1880static int sx_tiocmset(struct tty_struct *tty, struct file *file,
1881 unsigned int set, unsigned int clear)
1882{
1883 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1884 unsigned long flags;
1885 struct specialix_board *bp;
1886
1887 func_enter();
1888
1889 if (sx_paranoia_check(port, tty->name, __FUNCTION__)) {
1890 func_exit();
1891 return -ENODEV;
1892 }
1893
1894 bp = port_Board(port);
1895
1896 spin_lock_irqsave(&port->lock, flags);
1897 /* if (set & TIOCM_RTS)
1898 port->MSVR |= MSVR_RTS; */
1899 /* if (set & TIOCM_DTR)
1900 port->MSVR |= MSVR_DTR; */
1901
1902 if (SX_CRTSCTS(port->tty)) {
1903 if (set & TIOCM_RTS)
1904 port->MSVR |= MSVR_DTR;
1905 } else {
1906 if (set & TIOCM_DTR)
1907 port->MSVR |= MSVR_DTR;
1908 }
1909
1910 /* if (clear & TIOCM_RTS)
1911 port->MSVR &= ~MSVR_RTS; */
1912 /* if (clear & TIOCM_DTR)
1913 port->MSVR &= ~MSVR_DTR; */
1914 if (SX_CRTSCTS(port->tty)) {
1915 if (clear & TIOCM_RTS)
1916 port->MSVR &= ~MSVR_DTR;
1917 } else {
1918 if (clear & TIOCM_DTR)
1919 port->MSVR &= ~MSVR_DTR;
1920 }
1921 spin_lock_irqsave(&bp->lock, flags);
1922 sx_out(bp, CD186x_CAR, port_No(port));
1923 sx_out(bp, CD186x_MSVR, port->MSVR);
1924 spin_unlock_irqrestore(&bp->lock, flags);
1925 spin_unlock_irqrestore(&port->lock, flags);
1926 func_exit();
1927 return 0;
1928}
1929
1930
1931static inline void sx_send_break(struct specialix_port * port, unsigned long length)
1932{
1933 struct specialix_board *bp = port_Board(port);
1934 unsigned long flags;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001935
Linus Torvalds1da177e2005-04-16 15:20:36 -07001936 func_enter();
1937
1938 spin_lock_irqsave (&port->lock, flags);
1939 port->break_length = SPECIALIX_TPS / HZ * length;
1940 port->COR2 |= COR2_ETC;
1941 port->IER |= IER_TXRDY;
1942 spin_lock_irqsave(&bp->lock, flags);
1943 sx_out(bp, CD186x_CAR, port_No(port));
1944 sx_out(bp, CD186x_COR2, port->COR2);
1945 sx_out(bp, CD186x_IER, port->IER);
1946 spin_unlock_irqrestore(&bp->lock, flags);
1947 spin_unlock_irqrestore (&port->lock, flags);
1948 sx_wait_CCR(bp);
1949 spin_lock_irqsave(&bp->lock, flags);
1950 sx_out(bp, CD186x_CCR, CCR_CORCHG2);
1951 spin_unlock_irqrestore(&bp->lock, flags);
1952 sx_wait_CCR(bp);
1953
1954 func_exit();
1955}
1956
1957
1958static inline int sx_set_serial_info(struct specialix_port * port,
1959 struct serial_struct __user * newinfo)
1960{
1961 struct serial_struct tmp;
1962 struct specialix_board *bp = port_Board(port);
1963 int change_speed;
1964
1965 func_enter();
1966 /*
Jesper Juhle49332b2005-05-01 08:59:08 -07001967 if (!access_ok(VERIFY_READ, (void *) newinfo, sizeof(tmp))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001968 func_exit();
Jesper Juhle49332b2005-05-01 08:59:08 -07001969 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001970 }
1971 */
1972 if (copy_from_user(&tmp, newinfo, sizeof(tmp))) {
1973 func_enter();
1974 return -EFAULT;
1975 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001976
1977#if 0
Linus Torvalds1da177e2005-04-16 15:20:36 -07001978 if ((tmp.irq != bp->irq) ||
1979 (tmp.port != bp->base) ||
1980 (tmp.type != PORT_CIRRUS) ||
1981 (tmp.baud_base != (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC) ||
1982 (tmp.custom_divisor != 0) ||
1983 (tmp.xmit_fifo_size != CD186x_NFIFO) ||
1984 (tmp.flags & ~SPECIALIX_LEGAL_FLAGS)) {
1985 func_exit();
1986 return -EINVAL;
1987 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001988#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001989
1990 change_speed = ((port->flags & ASYNC_SPD_MASK) !=
1991 (tmp.flags & ASYNC_SPD_MASK));
1992 change_speed |= (tmp.custom_divisor != port->custom_divisor);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001993
Linus Torvalds1da177e2005-04-16 15:20:36 -07001994 if (!capable(CAP_SYS_ADMIN)) {
1995 if ((tmp.close_delay != port->close_delay) ||
1996 (tmp.closing_wait != port->closing_wait) ||
1997 ((tmp.flags & ~ASYNC_USR_MASK) !=
1998 (port->flags & ~ASYNC_USR_MASK))) {
1999 func_exit();
2000 return -EPERM;
2001 }
2002 port->flags = ((port->flags & ~ASYNC_USR_MASK) |
2003 (tmp.flags & ASYNC_USR_MASK));
2004 port->custom_divisor = tmp.custom_divisor;
2005 } else {
2006 port->flags = ((port->flags & ~ASYNC_FLAGS) |
2007 (tmp.flags & ASYNC_FLAGS));
2008 port->close_delay = tmp.close_delay;
2009 port->closing_wait = tmp.closing_wait;
2010 port->custom_divisor = tmp.custom_divisor;
2011 }
2012 if (change_speed) {
2013 sx_change_speed(bp, port);
2014 }
2015 func_exit();
2016 return 0;
2017}
2018
2019
2020static inline int sx_get_serial_info(struct specialix_port * port,
2021 struct serial_struct __user *retinfo)
2022{
2023 struct serial_struct tmp;
2024 struct specialix_board *bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002025
Linus Torvalds1da177e2005-04-16 15:20:36 -07002026 func_enter();
2027
2028 /*
Jesper Juhle49332b2005-05-01 08:59:08 -07002029 if (!access_ok(VERIFY_WRITE, (void *) retinfo, sizeof(tmp)))
2030 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002031 */
2032
2033 memset(&tmp, 0, sizeof(tmp));
2034 tmp.type = PORT_CIRRUS;
2035 tmp.line = port - sx_port;
2036 tmp.port = bp->base;
2037 tmp.irq = bp->irq;
2038 tmp.flags = port->flags;
2039 tmp.baud_base = (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC;
2040 tmp.close_delay = port->close_delay * HZ/100;
2041 tmp.closing_wait = port->closing_wait * HZ/100;
2042 tmp.custom_divisor = port->custom_divisor;
2043 tmp.xmit_fifo_size = CD186x_NFIFO;
2044 if (copy_to_user(retinfo, &tmp, sizeof(tmp))) {
2045 func_exit();
2046 return -EFAULT;
2047 }
2048
2049 func_exit();
2050 return 0;
2051}
2052
2053
Jeff Garzikd61780c2005-10-30 15:01:51 -08002054static int sx_ioctl(struct tty_struct * tty, struct file * filp,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002055 unsigned int cmd, unsigned long arg)
2056{
2057 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2058 int retval;
2059 void __user *argp = (void __user *)arg;
2060
2061 func_enter();
2062
2063 if (sx_paranoia_check(port, tty->name, "sx_ioctl")) {
2064 func_exit();
2065 return -ENODEV;
2066 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002067
Linus Torvalds1da177e2005-04-16 15:20:36 -07002068 switch (cmd) {
2069 case TCSBRK: /* SVID version: non-zero arg --> no break */
2070 retval = tty_check_change(tty);
2071 if (retval) {
2072 func_exit();
2073 return retval;
2074 }
2075 tty_wait_until_sent(tty, 0);
2076 if (!arg)
2077 sx_send_break(port, HZ/4); /* 1/4 second */
2078 return 0;
2079 case TCSBRKP: /* support for POSIX tcsendbreak() */
2080 retval = tty_check_change(tty);
2081 if (retval) {
2082 func_exit();
2083 return retval;
2084 }
2085 tty_wait_until_sent(tty, 0);
2086 sx_send_break(port, arg ? arg*(HZ/10) : HZ/4);
2087 func_exit();
2088 return 0;
2089 case TIOCGSOFTCAR:
2090 if (put_user(C_CLOCAL(tty)?1:0, (unsigned long __user *)argp)) {
2091 func_exit();
2092 return -EFAULT;
2093 }
2094 func_exit();
2095 return 0;
2096 case TIOCSSOFTCAR:
2097 if (get_user(arg, (unsigned long __user *) argp)) {
2098 func_exit();
2099 return -EFAULT;
2100 }
2101 tty->termios->c_cflag =
2102 ((tty->termios->c_cflag & ~CLOCAL) |
2103 (arg ? CLOCAL : 0));
2104 func_exit();
2105 return 0;
2106 case TIOCGSERIAL:
2107 func_exit();
2108 return sx_get_serial_info(port, argp);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002109 case TIOCSSERIAL:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002110 func_exit();
2111 return sx_set_serial_info(port, argp);
2112 default:
2113 func_exit();
2114 return -ENOIOCTLCMD;
2115 }
2116 func_exit();
2117 return 0;
2118}
2119
2120
2121static void sx_throttle(struct tty_struct * tty)
2122{
2123 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2124 struct specialix_board *bp;
2125 unsigned long flags;
2126
2127 func_enter();
2128
2129 if (sx_paranoia_check(port, tty->name, "sx_throttle")) {
2130 func_exit();
2131 return;
2132 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002133
Linus Torvalds1da177e2005-04-16 15:20:36 -07002134 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002135
Linus Torvalds1da177e2005-04-16 15:20:36 -07002136 /* Use DTR instead of RTS ! */
Jeff Garzikd61780c2005-10-30 15:01:51 -08002137 if (SX_CRTSCTS (tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002138 port->MSVR &= ~MSVR_DTR;
2139 else {
2140 /* Auch!!! I think the system shouldn't call this then. */
2141 /* Or maybe we're supposed (allowed?) to do our side of hw
Jeff Garzikd61780c2005-10-30 15:01:51 -08002142 handshake anyway, even when hardware handshake is off.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002143 When you see this in your logs, please report.... */
2144 printk (KERN_ERR "sx%d: Need to throttle, but can't (hardware hs is off)\n",
2145 port_No (port));
2146 }
2147 spin_lock_irqsave(&bp->lock, flags);
2148 sx_out(bp, CD186x_CAR, port_No(port));
2149 spin_unlock_irqrestore(&bp->lock, flags);
2150 if (I_IXOFF(tty)) {
2151 spin_unlock_irqrestore(&bp->lock, flags);
2152 sx_wait_CCR(bp);
2153 spin_lock_irqsave(&bp->lock, flags);
2154 sx_out(bp, CD186x_CCR, CCR_SSCH2);
2155 spin_unlock_irqrestore(&bp->lock, flags);
2156 sx_wait_CCR(bp);
2157 }
2158 spin_lock_irqsave(&bp->lock, flags);
2159 sx_out(bp, CD186x_MSVR, port->MSVR);
2160 spin_unlock_irqrestore(&bp->lock, flags);
2161
2162 func_exit();
2163}
2164
2165
2166static void sx_unthrottle(struct tty_struct * tty)
2167{
2168 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2169 struct specialix_board *bp;
2170 unsigned long flags;
2171
2172 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002173
Linus Torvalds1da177e2005-04-16 15:20:36 -07002174 if (sx_paranoia_check(port, tty->name, "sx_unthrottle")) {
2175 func_exit();
2176 return;
2177 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002178
Linus Torvalds1da177e2005-04-16 15:20:36 -07002179 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002180
Linus Torvalds1da177e2005-04-16 15:20:36 -07002181 spin_lock_irqsave(&port->lock, flags);
2182 /* XXXX Use DTR INSTEAD???? */
2183 if (SX_CRTSCTS(tty)) {
2184 port->MSVR |= MSVR_DTR;
2185 } /* Else clause: see remark in "sx_throttle"... */
2186 spin_lock_irqsave(&bp->lock, flags);
2187 sx_out(bp, CD186x_CAR, port_No(port));
2188 spin_unlock_irqrestore(&bp->lock, flags);
2189 if (I_IXOFF(tty)) {
2190 spin_unlock_irqrestore(&port->lock, flags);
2191 sx_wait_CCR(bp);
2192 spin_lock_irqsave(&bp->lock, flags);
2193 sx_out(bp, CD186x_CCR, CCR_SSCH1);
2194 spin_unlock_irqrestore(&bp->lock, flags);
2195 sx_wait_CCR(bp);
2196 spin_lock_irqsave(&port->lock, flags);
2197 }
2198 spin_lock_irqsave(&bp->lock, flags);
2199 sx_out(bp, CD186x_MSVR, port->MSVR);
2200 spin_unlock_irqrestore(&bp->lock, flags);
2201 spin_unlock_irqrestore(&port->lock, flags);
2202
2203 func_exit();
2204}
2205
2206
2207static void sx_stop(struct tty_struct * tty)
2208{
2209 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2210 struct specialix_board *bp;
2211 unsigned long flags;
2212
2213 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002214
Linus Torvalds1da177e2005-04-16 15:20:36 -07002215 if (sx_paranoia_check(port, tty->name, "sx_stop")) {
2216 func_exit();
2217 return;
2218 }
2219
2220 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002221
Linus Torvalds1da177e2005-04-16 15:20:36 -07002222 spin_lock_irqsave(&port->lock, flags);
2223 port->IER &= ~IER_TXRDY;
2224 spin_lock_irqsave(&bp->lock, flags);
2225 sx_out(bp, CD186x_CAR, port_No(port));
2226 sx_out(bp, CD186x_IER, port->IER);
2227 spin_unlock_irqrestore(&bp->lock, flags);
2228 spin_unlock_irqrestore(&port->lock, flags);
2229
2230 func_exit();
2231}
2232
2233
2234static void sx_start(struct tty_struct * tty)
2235{
2236 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2237 struct specialix_board *bp;
2238 unsigned long flags;
2239
2240 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002241
Linus Torvalds1da177e2005-04-16 15:20:36 -07002242 if (sx_paranoia_check(port, tty->name, "sx_start")) {
2243 func_exit();
2244 return;
2245 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002246
Linus Torvalds1da177e2005-04-16 15:20:36 -07002247 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002248
Linus Torvalds1da177e2005-04-16 15:20:36 -07002249 spin_lock_irqsave(&port->lock, flags);
2250 if (port->xmit_cnt && port->xmit_buf && !(port->IER & IER_TXRDY)) {
2251 port->IER |= IER_TXRDY;
2252 spin_lock_irqsave(&bp->lock, flags);
2253 sx_out(bp, CD186x_CAR, port_No(port));
2254 sx_out(bp, CD186x_IER, port->IER);
2255 spin_unlock_irqrestore(&bp->lock, flags);
2256 }
2257 spin_unlock_irqrestore(&port->lock, flags);
2258
2259 func_exit();
2260}
2261
2262
2263/*
2264 * This routine is called from the work-queue when the interrupt
2265 * routine has signalled that a hangup has occurred. The path of
2266 * hangup processing is:
2267 *
2268 * serial interrupt routine -> (workqueue) ->
2269 * do_sx_hangup() -> tty->hangup() -> sx_hangup()
Jeff Garzikd61780c2005-10-30 15:01:51 -08002270 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07002271 */
2272static void do_sx_hangup(void *private_)
2273{
2274 struct specialix_port *port = (struct specialix_port *) private_;
2275 struct tty_struct *tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002276
Linus Torvalds1da177e2005-04-16 15:20:36 -07002277 func_enter();
2278
2279 tty = port->tty;
2280 if (tty)
2281 tty_hangup(tty); /* FIXME: module removal race here */
2282
2283 func_exit();
2284}
2285
2286
2287static void sx_hangup(struct tty_struct * tty)
2288{
2289 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2290 struct specialix_board *bp;
2291 unsigned long flags;
2292
2293 func_enter();
2294
2295 if (sx_paranoia_check(port, tty->name, "sx_hangup")) {
2296 func_exit();
2297 return;
2298 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002299
Linus Torvalds1da177e2005-04-16 15:20:36 -07002300 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002301
Linus Torvalds1da177e2005-04-16 15:20:36 -07002302 sx_shutdown_port(bp, port);
2303 spin_lock_irqsave(&port->lock, flags);
2304 port->event = 0;
2305 bp->count -= port->count;
2306 if (bp->count < 0) {
2307 printk(KERN_ERR "sx%d: sx_hangup: bad board count: %d port: %d\n",
2308 board_No(bp), bp->count, tty->index);
2309 bp->count = 0;
2310 }
2311 port->count = 0;
2312 port->flags &= ~ASYNC_NORMAL_ACTIVE;
2313 port->tty = NULL;
2314 spin_unlock_irqrestore(&port->lock, flags);
2315 wake_up_interruptible(&port->open_wait);
2316
2317 func_exit();
2318}
2319
2320
2321static void sx_set_termios(struct tty_struct * tty, struct termios * old_termios)
2322{
2323 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2324 unsigned long flags;
2325 struct specialix_board * bp;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002326
Linus Torvalds1da177e2005-04-16 15:20:36 -07002327 if (sx_paranoia_check(port, tty->name, "sx_set_termios"))
2328 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002329
Linus Torvalds1da177e2005-04-16 15:20:36 -07002330 if (tty->termios->c_cflag == old_termios->c_cflag &&
2331 tty->termios->c_iflag == old_termios->c_iflag)
2332 return;
2333
2334 bp = port_Board(port);
2335 spin_lock_irqsave(&port->lock, flags);
2336 sx_change_speed(port_Board(port), port);
2337 spin_unlock_irqrestore(&port->lock, flags);
2338
2339 if ((old_termios->c_cflag & CRTSCTS) &&
2340 !(tty->termios->c_cflag & CRTSCTS)) {
2341 tty->hw_stopped = 0;
2342 sx_start(tty);
2343 }
2344}
2345
2346
2347static void do_softint(void *private_)
2348{
2349 struct specialix_port *port = (struct specialix_port *) private_;
2350 struct tty_struct *tty;
2351
2352 func_enter();
2353
2354 if(!(tty = port->tty)) {
2355 func_exit();
2356 return;
2357 }
2358
2359 if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) {
2360 tty_wakeup(tty);
2361 //wake_up_interruptible(&tty->write_wait);
2362 }
2363
2364 func_exit();
2365}
2366
2367static struct tty_operations sx_ops = {
2368 .open = sx_open,
2369 .close = sx_close,
2370 .write = sx_write,
2371 .put_char = sx_put_char,
2372 .flush_chars = sx_flush_chars,
2373 .write_room = sx_write_room,
2374 .chars_in_buffer = sx_chars_in_buffer,
2375 .flush_buffer = sx_flush_buffer,
2376 .ioctl = sx_ioctl,
2377 .throttle = sx_throttle,
2378 .unthrottle = sx_unthrottle,
2379 .set_termios = sx_set_termios,
2380 .stop = sx_stop,
2381 .start = sx_start,
2382 .hangup = sx_hangup,
2383 .tiocmget = sx_tiocmget,
2384 .tiocmset = sx_tiocmset,
2385};
2386
2387static int sx_init_drivers(void)
2388{
2389 int error;
2390 int i;
2391
2392 func_enter();
2393
2394 specialix_driver = alloc_tty_driver(SX_NBOARD * SX_NPORT);
2395 if (!specialix_driver) {
2396 printk(KERN_ERR "sx: Couldn't allocate tty_driver.\n");
2397 func_exit();
2398 return 1;
2399 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002400
Linus Torvalds1da177e2005-04-16 15:20:36 -07002401 if (!(tmp_buf = (unsigned char *) get_zeroed_page(GFP_KERNEL))) {
2402 printk(KERN_ERR "sx: Couldn't get free page.\n");
2403 put_tty_driver(specialix_driver);
2404 func_exit();
2405 return 1;
2406 }
2407 specialix_driver->owner = THIS_MODULE;
2408 specialix_driver->name = "ttyW";
2409 specialix_driver->major = SPECIALIX_NORMAL_MAJOR;
2410 specialix_driver->type = TTY_DRIVER_TYPE_SERIAL;
2411 specialix_driver->subtype = SERIAL_TYPE_NORMAL;
2412 specialix_driver->init_termios = tty_std_termios;
2413 specialix_driver->init_termios.c_cflag =
2414 B9600 | CS8 | CREAD | HUPCL | CLOCAL;
2415 specialix_driver->flags = TTY_DRIVER_REAL_RAW;
2416 tty_set_operations(specialix_driver, &sx_ops);
2417
2418 if ((error = tty_register_driver(specialix_driver))) {
2419 put_tty_driver(specialix_driver);
2420 free_page((unsigned long)tmp_buf);
2421 printk(KERN_ERR "sx: Couldn't register specialix IO8+ driver, error = %d\n",
2422 error);
2423 func_exit();
2424 return 1;
2425 }
2426 memset(sx_port, 0, sizeof(sx_port));
2427 for (i = 0; i < SX_NPORT * SX_NBOARD; i++) {
2428 sx_port[i].magic = SPECIALIX_MAGIC;
2429 INIT_WORK(&sx_port[i].tqueue, do_softint, &sx_port[i]);
2430 INIT_WORK(&sx_port[i].tqueue_hangup, do_sx_hangup, &sx_port[i]);
2431 sx_port[i].close_delay = 50 * HZ/100;
2432 sx_port[i].closing_wait = 3000 * HZ/100;
2433 init_waitqueue_head(&sx_port[i].open_wait);
2434 init_waitqueue_head(&sx_port[i].close_wait);
2435 spin_lock_init(&sx_port[i].lock);
2436 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002437
Linus Torvalds1da177e2005-04-16 15:20:36 -07002438 func_exit();
2439 return 0;
2440}
2441
2442static void sx_release_drivers(void)
2443{
2444 func_enter();
2445
2446 free_page((unsigned long)tmp_buf);
2447 tty_unregister_driver(specialix_driver);
2448 put_tty_driver(specialix_driver);
2449 func_exit();
2450}
2451
Jeff Garzikd61780c2005-10-30 15:01:51 -08002452/*
2453 * This routine must be called by kernel at boot time
Linus Torvalds1da177e2005-04-16 15:20:36 -07002454 */
2455static int __init specialix_init(void)
2456{
2457 int i;
2458 int found = 0;
2459
2460 func_enter();
2461
2462 printk(KERN_INFO "sx: Specialix IO8+ driver v" VERSION ", (c) R.E.Wolff 1997/1998.\n");
2463 printk(KERN_INFO "sx: derived from work (c) D.Gorodchanin 1994-1996.\n");
2464#ifdef CONFIG_SPECIALIX_RTSCTS
2465 printk (KERN_INFO "sx: DTR/RTS pin is always RTS.\n");
2466#else
2467 printk (KERN_INFO "sx: DTR/RTS pin is RTS when CRTSCTS is on.\n");
2468#endif
Jeff Garzikd61780c2005-10-30 15:01:51 -08002469
Linus Torvalds1da177e2005-04-16 15:20:36 -07002470 for (i = 0; i < SX_NBOARD; i++)
Ingo Molnar34af9462006-06-27 02:53:55 -07002471 spin_lock_init(&sx_board[i].lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002472
2473 if (sx_init_drivers()) {
2474 func_exit();
2475 return -EIO;
2476 }
2477
Jeff Garzikd61780c2005-10-30 15:01:51 -08002478 for (i = 0; i < SX_NBOARD; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002479 if (sx_board[i].base && !sx_probe(&sx_board[i]))
2480 found++;
2481
2482#ifdef CONFIG_PCI
2483 {
2484 struct pci_dev *pdev = NULL;
2485
2486 i=0;
2487 while (i < SX_NBOARD) {
2488 if (sx_board[i].flags & SX_BOARD_PRESENT) {
2489 i++;
2490 continue;
2491 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002492 pdev = pci_find_device (PCI_VENDOR_ID_SPECIALIX,
2493 PCI_DEVICE_ID_SPECIALIX_IO8,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002494 pdev);
2495 if (!pdev) break;
2496
2497 if (pci_enable_device(pdev))
2498 continue;
2499
2500 sx_board[i].irq = pdev->irq;
2501
2502 sx_board[i].base = pci_resource_start (pdev, 2);
2503
2504 sx_board[i].flags |= SX_BOARD_IS_PCI;
2505 if (!sx_probe(&sx_board[i]))
2506 found ++;
2507 }
2508 }
2509#endif
2510
2511 if (!found) {
2512 sx_release_drivers();
2513 printk(KERN_INFO "sx: No specialix IO8+ boards detected.\n");
2514 func_exit();
2515 return -EIO;
2516 }
2517
2518 func_exit();
2519 return 0;
2520}
2521
2522static int iobase[SX_NBOARD] = {0,};
2523
2524static int irq [SX_NBOARD] = {0,};
2525
2526module_param_array(iobase, int, NULL, 0);
2527module_param_array(irq, int, NULL, 0);
2528module_param(sx_debug, int, 0);
2529module_param(sx_rxfifo, int, 0);
2530#ifdef SPECIALIX_TIMER
2531module_param(sx_poll, int, 0);
2532#endif
2533
2534/*
2535 * You can setup up to 4 boards.
2536 * by specifying "iobase=0xXXX,0xXXX ..." as insmod parameter.
Jeff Garzikd61780c2005-10-30 15:01:51 -08002537 * You should specify the IRQs too in that case "irq=....,...".
2538 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07002539 * More than 4 boards in one computer is not possible, as the card can
Jeff Garzikd61780c2005-10-30 15:01:51 -08002540 * only use 4 different interrupts.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002541 *
2542 */
2543static int __init specialix_init_module(void)
2544{
2545 int i;
2546
2547 func_enter();
2548
Linus Torvalds1da177e2005-04-16 15:20:36 -07002549 if (iobase[0] || iobase[1] || iobase[2] || iobase[3]) {
2550 for(i = 0; i < SX_NBOARD; i++) {
2551 sx_board[i].base = iobase[i];
2552 sx_board[i].irq = irq[i];
2553 sx_board[i].count= 0;
2554 }
2555 }
2556
2557 func_exit();
2558
2559 return specialix_init();
2560}
Jeff Garzikd61780c2005-10-30 15:01:51 -08002561
Linus Torvalds1da177e2005-04-16 15:20:36 -07002562static void __exit specialix_exit_module(void)
2563{
2564 int i;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002565
Linus Torvalds1da177e2005-04-16 15:20:36 -07002566 func_enter();
2567
2568 sx_release_drivers();
2569 for (i = 0; i < SX_NBOARD; i++)
Jeff Garzikd61780c2005-10-30 15:01:51 -08002570 if (sx_board[i].flags & SX_BOARD_PRESENT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002571 sx_release_io_range(&sx_board[i]);
2572#ifdef SPECIALIX_TIMER
2573 del_timer (&missed_irq_timer);
2574#endif
2575
2576 func_exit();
2577}
2578
Chuck Short7691030b2006-07-10 04:43:59 -07002579static struct pci_device_id specialx_pci_tbl[] __devinitdata = {
2580 { PCI_DEVICE(PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_SPECIALIX_IO8) },
2581 { }
2582};
2583MODULE_DEVICE_TABLE(pci, specialx_pci_tbl);
2584
Linus Torvalds1da177e2005-04-16 15:20:36 -07002585module_init(specialix_init_module);
2586module_exit(specialix_exit_module);
2587
2588MODULE_LICENSE("GPL");