blob: f52c7c31badf0544bd7024aa12d8feaffeeb4699 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * specialix.c -- specialix IO8+ multiport serial driver.
3 *
4 * Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl)
5 * Copyright (C) 1994-1996 Dmitry Gorodchanin (pgmdsg@ibi.com)
6 *
7 * Specialix pays for the development and support of this driver.
8 * Please DO contact io8-linux@specialix.co.uk if you require
9 * support. But please read the documentation (specialix.txt)
10 * first.
11 *
12 * This driver was developped in the BitWizard linux device
13 * driver service. If you require a linux device driver for your
14 * product, please contact devices@BitWizard.nl for a quote.
15 *
16 * This code is firmly based on the riscom/8 serial driver,
17 * written by Dmitry Gorodchanin. The specialix IO8+ card
18 * programming information was obtained from the CL-CD1865 Data
19 * Book, and Specialix document number 6200059: IO8+ Hardware
20 * Functional Specification.
21 *
22 * This program is free software; you can redistribute it and/or
23 * modify it under the terms of the GNU General Public License as
24 * published by the Free Software Foundation; either version 2 of
25 * the License, or (at your option) any later version.
26 *
27 * This program is distributed in the hope that it will be
28 * useful, but WITHOUT ANY WARRANTY; without even the implied
29 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
30 * PURPOSE. See the GNU General Public License for more details.
31 *
32 * You should have received a copy of the GNU General Public
33 * License along with this program; if not, write to the Free
34 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
35 * USA.
36 *
37 * Revision history:
38 *
39 * Revision 1.0: April 1st 1997.
40 * Initial release for alpha testing.
Jeff Garzikd61780c2005-10-30 15:01:51 -080041 * Revision 1.1: April 14th 1997.
42 * Incorporated Richard Hudsons suggestions,
Linus Torvalds1da177e2005-04-16 15:20:36 -070043 * removed some debugging printk's.
44 * Revision 1.2: April 15th 1997.
45 * Ported to 2.1.x kernels.
Jeff Garzikd61780c2005-10-30 15:01:51 -080046 * Revision 1.3: April 17th 1997
47 * Backported to 2.0. (Compatibility macros).
Linus Torvalds1da177e2005-04-16 15:20:36 -070048 * Revision 1.4: April 18th 1997
Jeff Garzikd61780c2005-10-30 15:01:51 -080049 * Fixed DTR/RTS bug that caused the card to indicate
50 * "don't send data" to a modem after the password prompt.
Linus Torvalds1da177e2005-04-16 15:20:36 -070051 * Fixed bug for premature (fake) interrupts.
52 * Revision 1.5: April 19th 1997
Jeff Garzikd61780c2005-10-30 15:01:51 -080053 * fixed a minor typo in the header file, cleanup a little.
Linus Torvalds1da177e2005-04-16 15:20:36 -070054 * performance warnings are now MAXed at once per minute.
55 * Revision 1.6: May 23 1997
56 * Changed the specialix=... format to include interrupt.
57 * Revision 1.7: May 27 1997
58 * Made many more debug printk's a compile time option.
59 * Revision 1.8: Jul 1 1997
60 * port to linux-2.1.43 kernel.
61 * Revision 1.9: Oct 9 1998
62 * Added stuff for the IO8+/PCI version.
Jeff Garzikd61780c2005-10-30 15:01:51 -080063 * Revision 1.10: Oct 22 1999 / Jan 21 2000.
64 * Added stuff for setserial.
Linus Torvalds1da177e2005-04-16 15:20:36 -070065 * Nicolas Mailhot (Nicolas.Mailhot@email.enst.fr)
Jeff Garzikd61780c2005-10-30 15:01:51 -080066 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070067 */
68
69#define VERSION "1.11"
70
71
72/*
73 * There is a bunch of documentation about the card, jumpers, config
74 * settings, restrictions, cables, device names and numbers in
75 * Documentation/specialix.txt
76 */
77
Linus Torvalds1da177e2005-04-16 15:20:36 -070078#include <linux/module.h>
79
80#include <asm/io.h>
81#include <linux/kernel.h>
82#include <linux/sched.h>
83#include <linux/ioport.h>
84#include <linux/interrupt.h>
85#include <linux/errno.h>
86#include <linux/tty.h>
Alan Cox33f0f882006-01-09 20:54:13 -080087#include <linux/tty_flip.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070088#include <linux/mm.h>
89#include <linux/serial.h>
90#include <linux/fcntl.h>
91#include <linux/major.h>
92#include <linux/delay.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070093#include <linux/pci.h>
94#include <linux/init.h>
95#include <asm/uaccess.h>
96
97#include "specialix_io8.h"
98#include "cd1865.h"
99
100
101/*
102 This driver can spew a whole lot of debugging output at you. If you
103 need maximum performance, you should disable the DEBUG define. To
104 aid in debugging in the field, I'm leaving the compile-time debug
105 features enabled, and disable them "runtime". That allows me to
106 instruct people with problems to enable debugging without requiring
107 them to recompile...
108*/
109#define DEBUG
110
111static int sx_debug;
112static int sx_rxfifo = SPECIALIX_RXFIFO;
113
114#ifdef DEBUG
115#define dprintk(f, str...) if (sx_debug & f) printk (str)
116#else
117#define dprintk(f, str...) /* nothing */
118#endif
119
120#define SX_DEBUG_FLOW 0x0001
121#define SX_DEBUG_DATA 0x0002
122#define SX_DEBUG_PROBE 0x0004
123#define SX_DEBUG_CHAN 0x0008
124#define SX_DEBUG_INIT 0x0010
125#define SX_DEBUG_RX 0x0020
126#define SX_DEBUG_TX 0x0040
127#define SX_DEBUG_IRQ 0x0080
128#define SX_DEBUG_OPEN 0x0100
129#define SX_DEBUG_TERMIOS 0x0200
130#define SX_DEBUG_SIGNALS 0x0400
131#define SX_DEBUG_FIFO 0x0800
132
133
134#define func_enter() dprintk (SX_DEBUG_FLOW, "io8: enter %s\n",__FUNCTION__)
135#define func_exit() dprintk (SX_DEBUG_FLOW, "io8: exit %s\n", __FUNCTION__)
136
137#define jiffies_from_ms(a) ((((a) * HZ)/1000)+1)
138
139
140/* Configurable options: */
141
142/* Am I paranoid or not ? ;-) */
143#define SPECIALIX_PARANOIA_CHECK
144
145/* Do I trust the IRQ from the card? (enabeling it doesn't seem to help)
146 When the IRQ routine leaves the chip in a state that is keeps on
147 requiring attention, the timer doesn't help either. */
148#undef SPECIALIX_TIMER
149
150#ifdef SPECIALIX_TIMER
151static int sx_poll = HZ;
152#endif
153
154
155
Jeff Garzikd61780c2005-10-30 15:01:51 -0800156/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157 * The following defines are mostly for testing purposes. But if you need
158 * some nice reporting in your syslog, you can define them also.
159 */
160#undef SX_REPORT_FIFO
161#undef SX_REPORT_OVERRUN
162
163
164
165#ifdef CONFIG_SPECIALIX_RTSCTS
166#define SX_CRTSCTS(bla) 1
167#else
168#define SX_CRTSCTS(tty) C_CRTSCTS(tty)
169#endif
170
171
172/* Used to be outb (0xff, 0x80); */
173#define short_pause() udelay (1)
174
175
176#define SPECIALIX_LEGAL_FLAGS \
177 (ASYNC_HUP_NOTIFY | ASYNC_SAK | ASYNC_SPLIT_TERMIOS | \
178 ASYNC_SPD_HI | ASYNC_SPEED_VHI | ASYNC_SESSION_LOCKOUT | \
179 ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP)
180
181#undef RS_EVENT_WRITE_WAKEUP
182#define RS_EVENT_WRITE_WAKEUP 0
183
184static struct tty_driver *specialix_driver;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185
186static unsigned long baud_table[] = {
187 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
Jeff Garzikd61780c2005-10-30 15:01:51 -0800188 9600, 19200, 38400, 57600, 115200, 0,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189};
190
191static struct specialix_board sx_board[SX_NBOARD] = {
192 { 0, SX_IOBASE1, 9, },
193 { 0, SX_IOBASE2, 11, },
194 { 0, SX_IOBASE3, 12, },
195 { 0, SX_IOBASE4, 15, },
196};
197
198static struct specialix_port sx_port[SX_NBOARD * SX_NPORT];
199
200
201#ifdef SPECIALIX_TIMER
202static struct timer_list missed_irq_timer;
203static irqreturn_t sx_interrupt(int irq, void * dev_id, struct pt_regs * regs);
204#endif
205
206
207
208static inline int sx_paranoia_check(struct specialix_port const * port,
209 char *name, const char *routine)
210{
211#ifdef SPECIALIX_PARANOIA_CHECK
212 static const char *badmagic =
213 KERN_ERR "sx: Warning: bad specialix port magic number for device %s in %s\n";
214 static const char *badinfo =
215 KERN_ERR "sx: Warning: null specialix port for device %s in %s\n";
Jeff Garzikd61780c2005-10-30 15:01:51 -0800216
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217 if (!port) {
218 printk(badinfo, name, routine);
219 return 1;
220 }
221 if (port->magic != SPECIALIX_MAGIC) {
222 printk(badmagic, name, routine);
223 return 1;
224 }
225#endif
226 return 0;
227}
228
229
230/*
Jeff Garzikd61780c2005-10-30 15:01:51 -0800231 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232 * Service functions for specialix IO8+ driver.
Jeff Garzikd61780c2005-10-30 15:01:51 -0800233 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234 */
235
236/* Get board number from pointer */
237static inline int board_No (struct specialix_board * bp)
238{
239 return bp - sx_board;
240}
241
242
243/* Get port number from pointer */
244static inline int port_No (struct specialix_port const * port)
245{
Jeff Garzikd61780c2005-10-30 15:01:51 -0800246 return SX_PORT(port - sx_port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247}
248
249
250/* Get pointer to board from pointer to port */
251static inline struct specialix_board * port_Board(struct specialix_port const * port)
252{
253 return &sx_board[SX_BOARD(port - sx_port)];
254}
255
256
257/* Input Byte from CL CD186x register */
258static inline unsigned char sx_in(struct specialix_board * bp, unsigned short reg)
259{
260 bp->reg = reg | 0x80;
261 outb (reg | 0x80, bp->base + SX_ADDR_REG);
262 return inb (bp->base + SX_DATA_REG);
263}
264
265
266/* Output Byte to CL CD186x register */
267static inline void sx_out(struct specialix_board * bp, unsigned short reg,
268 unsigned char val)
269{
270 bp->reg = reg | 0x80;
271 outb (reg | 0x80, bp->base + SX_ADDR_REG);
272 outb (val, bp->base + SX_DATA_REG);
273}
274
275
276/* Input Byte from CL CD186x register */
277static inline unsigned char sx_in_off(struct specialix_board * bp, unsigned short reg)
278{
279 bp->reg = reg;
280 outb (reg, bp->base + SX_ADDR_REG);
281 return inb (bp->base + SX_DATA_REG);
282}
283
284
285/* Output Byte to CL CD186x register */
286static inline void sx_out_off(struct specialix_board * bp, unsigned short reg,
287 unsigned char val)
288{
289 bp->reg = reg;
290 outb (reg, bp->base + SX_ADDR_REG);
291 outb (val, bp->base + SX_DATA_REG);
292}
293
294
295/* Wait for Channel Command Register ready */
296static inline void sx_wait_CCR(struct specialix_board * bp)
297{
298 unsigned long delay, flags;
299 unsigned char ccr;
300
301 for (delay = SX_CCR_TIMEOUT; delay; delay--) {
302 spin_lock_irqsave(&bp->lock, flags);
303 ccr = sx_in(bp, CD186x_CCR);
304 spin_unlock_irqrestore(&bp->lock, flags);
305 if (!ccr)
306 return;
307 udelay (1);
308 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800309
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310 printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp));
311}
312
313
314/* Wait for Channel Command Register ready */
315static inline void sx_wait_CCR_off(struct specialix_board * bp)
316{
317 unsigned long delay;
318 unsigned char crr;
319 unsigned long flags;
320
321 for (delay = SX_CCR_TIMEOUT; delay; delay--) {
322 spin_lock_irqsave(&bp->lock, flags);
323 crr = sx_in_off(bp, CD186x_CCR);
324 spin_unlock_irqrestore(&bp->lock, flags);
325 if (!crr)
326 return;
327 udelay (1);
328 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800329
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp));
331}
332
333
334/*
335 * specialix IO8+ IO range functions.
336 */
337
Jeff Garzikd61780c2005-10-30 15:01:51 -0800338static inline int sx_request_io_range(struct specialix_board * bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339{
Jeff Garzikd61780c2005-10-30 15:01:51 -0800340 return request_region(bp->base,
341 bp->flags & SX_BOARD_IS_PCI ? SX_PCI_IO_SPACE : SX_IO_SPACE,
342 "specialix IO8+") == NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343}
344
345
346static inline void sx_release_io_range(struct specialix_board * bp)
347{
Jeff Garzikd61780c2005-10-30 15:01:51 -0800348 release_region(bp->base,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349 bp->flags&SX_BOARD_IS_PCI?SX_PCI_IO_SPACE:SX_IO_SPACE);
350}
351
Jeff Garzikd61780c2005-10-30 15:01:51 -0800352
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353/* Must be called with enabled interrupts */
Jeff Garzikd61780c2005-10-30 15:01:51 -0800354/* Ugly. Very ugly. Don't use this for anything else than initialization
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355 code */
356static inline void sx_long_delay(unsigned long delay)
357{
358 unsigned long i;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800359
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360 for (i = jiffies + delay; time_after(i, jiffies); ) ;
361}
362
363
364
365/* Set the IRQ using the RTS lines that run to the PAL on the board.... */
366static int sx_set_irq ( struct specialix_board *bp)
367{
368 int virq;
369 int i;
370 unsigned long flags;
371
Jeff Garzikd61780c2005-10-30 15:01:51 -0800372 if (bp->flags & SX_BOARD_IS_PCI)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373 return 1;
374 switch (bp->irq) {
375 /* In the same order as in the docs... */
376 case 15: virq = 0;break;
377 case 12: virq = 1;break;
378 case 11: virq = 2;break;
379 case 9: virq = 3;break;
380 default: printk (KERN_ERR "Speclialix: cannot set irq to %d.\n", bp->irq);
381 return 0;
382 }
383 spin_lock_irqsave(&bp->lock, flags);
384 for (i=0;i<2;i++) {
385 sx_out(bp, CD186x_CAR, i);
386 sx_out(bp, CD186x_MSVRTS, ((virq >> i) & 0x1)? MSVR_RTS:0);
387 }
388 spin_unlock_irqrestore(&bp->lock, flags);
389 return 1;
390}
391
392
393/* Reset and setup CD186x chip */
394static int sx_init_CD186x(struct specialix_board * bp)
395{
396 unsigned long flags;
397 int scaler;
398 int rv = 1;
399
400 func_enter();
401 sx_wait_CCR_off(bp); /* Wait for CCR ready */
402 spin_lock_irqsave(&bp->lock, flags);
403 sx_out_off(bp, CD186x_CCR, CCR_HARDRESET); /* Reset CD186x chip */
404 spin_unlock_irqrestore(&bp->lock, flags);
405 sx_long_delay(HZ/20); /* Delay 0.05 sec */
406 spin_lock_irqsave(&bp->lock, flags);
407 sx_out_off(bp, CD186x_GIVR, SX_ID); /* Set ID for this chip */
408 sx_out_off(bp, CD186x_GICR, 0); /* Clear all bits */
409 sx_out_off(bp, CD186x_PILR1, SX_ACK_MINT); /* Prio for modem intr */
410 sx_out_off(bp, CD186x_PILR2, SX_ACK_TINT); /* Prio for transmitter intr */
411 sx_out_off(bp, CD186x_PILR3, SX_ACK_RINT); /* Prio for receiver intr */
412 /* Set RegAckEn */
413 sx_out_off(bp, CD186x_SRCR, sx_in (bp, CD186x_SRCR) | SRCR_REGACKEN);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800414
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415 /* Setting up prescaler. We need 4 ticks per 1 ms */
416 scaler = SX_OSCFREQ/SPECIALIX_TPS;
417
418 sx_out_off(bp, CD186x_PPRH, scaler >> 8);
419 sx_out_off(bp, CD186x_PPRL, scaler & 0xff);
420 spin_unlock_irqrestore(&bp->lock, flags);
421
422 if (!sx_set_irq (bp)) {
423 /* Figure out how to pass this along... */
424 printk (KERN_ERR "Cannot set irq to %d.\n", bp->irq);
425 rv = 0;
426 }
427
428 func_exit();
429 return rv;
430}
431
432
433static int read_cross_byte (struct specialix_board *bp, int reg, int bit)
434{
435 int i;
436 int t;
437 unsigned long flags;
438
439 spin_lock_irqsave(&bp->lock, flags);
440 for (i=0, t=0;i<8;i++) {
441 sx_out_off (bp, CD186x_CAR, i);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800442 if (sx_in_off (bp, reg) & bit)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443 t |= 1 << i;
444 }
445 spin_unlock_irqrestore(&bp->lock, flags);
446
447 return t;
448}
449
450
451#ifdef SPECIALIX_TIMER
452void missed_irq (unsigned long data)
453{
454 unsigned char irq;
455 unsigned long flags;
456 struct specialix_board *bp = (struct specialix_board *)data;
457
458 spin_lock_irqsave(&bp->lock, flags);
459 irq = sx_in ((struct specialix_board *)data, CD186x_SRSR) &
460 (SRSR_RREQint |
461 SRSR_TREQint |
462 SRSR_MREQint);
463 spin_unlock_irqrestore(&bp->lock, flags);
464 if (irq) {
465 printk (KERN_INFO "Missed interrupt... Calling int from timer. \n");
Jeff Garzikd61780c2005-10-30 15:01:51 -0800466 sx_interrupt (((struct specialix_board *)data)->irq,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467 (void*)data, NULL);
468 }
469 missed_irq_timer.expires = jiffies + sx_poll;
470 add_timer (&missed_irq_timer);
471}
472#endif
473
474
475
476/* Main probing routine, also sets irq. */
477static int sx_probe(struct specialix_board *bp)
478{
479 unsigned char val1, val2;
480#if 0
481 int irqs = 0;
482 int retries;
483#endif
484 int rev;
485 int chip;
486
487 func_enter();
488
Jeff Garzikd61780c2005-10-30 15:01:51 -0800489 if (sx_request_io_range(bp)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490 func_exit();
491 return 1;
492 }
493
494 /* Are the I/O ports here ? */
495 sx_out_off(bp, CD186x_PPRL, 0x5a);
496 short_pause ();
497 val1 = sx_in_off(bp, CD186x_PPRL);
498
499 sx_out_off(bp, CD186x_PPRL, 0xa5);
500 short_pause ();
501 val2 = sx_in_off(bp, CD186x_PPRL);
502
Jeff Garzikd61780c2005-10-30 15:01:51 -0800503
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 if ((val1 != 0x5a) || (val2 != 0xa5)) {
505 printk(KERN_INFO "sx%d: specialix IO8+ Board at 0x%03x not found.\n",
506 board_No(bp), bp->base);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800507 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508 func_exit();
509 return 1;
510 }
511
Jeff Garzikd61780c2005-10-30 15:01:51 -0800512 /* Check the DSR lines that Specialix uses as board
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 identification */
514 val1 = read_cross_byte (bp, CD186x_MSVR, MSVR_DSR);
515 val2 = read_cross_byte (bp, CD186x_MSVR, MSVR_RTS);
516 dprintk (SX_DEBUG_INIT, "sx%d: DSR lines are: %02x, rts lines are: %02x\n",
517 board_No(bp), val1, val2);
518
519 /* They managed to switch the bit order between the docs and
520 the IO8+ card. The new PCI card now conforms to old docs.
521 They changed the PCI docs to reflect the situation on the
522 old card. */
523 val2 = (bp->flags & SX_BOARD_IS_PCI)?0x4d : 0xb2;
524 if (val1 != val2) {
525 printk(KERN_INFO "sx%d: specialix IO8+ ID %02x at 0x%03x not found (%02x).\n",
526 board_No(bp), val2, bp->base, val1);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800527 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 func_exit();
529 return 1;
530 }
531
532
533#if 0
534 /* It's time to find IRQ for this board */
535 for (retries = 0; retries < 5 && irqs <= 0; retries++) {
536 irqs = probe_irq_on();
537 sx_init_CD186x(bp); /* Reset CD186x chip */
538 sx_out(bp, CD186x_CAR, 2); /* Select port 2 */
539 sx_wait_CCR(bp);
540 sx_out(bp, CD186x_CCR, CCR_TXEN); /* Enable transmitter */
541 sx_out(bp, CD186x_IER, IER_TXRDY); /* Enable tx empty intr */
Jeff Garzikd61780c2005-10-30 15:01:51 -0800542 sx_long_delay(HZ/20);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 irqs = probe_irq_off(irqs);
544
545 dprintk (SX_DEBUG_INIT, "SRSR = %02x, ", sx_in(bp, CD186x_SRSR));
546 dprintk (SX_DEBUG_INIT, "TRAR = %02x, ", sx_in(bp, CD186x_TRAR));
547 dprintk (SX_DEBUG_INIT, "GIVR = %02x, ", sx_in(bp, CD186x_GIVR));
548 dprintk (SX_DEBUG_INIT, "GICR = %02x, ", sx_in(bp, CD186x_GICR));
549 dprintk (SX_DEBUG_INIT, "\n");
550
551 /* Reset CD186x again */
552 if (!sx_init_CD186x(bp)) {
553 /* Hmmm. This is dead code anyway. */
554 }
555
556 dprintk (SX_DEBUG_INIT "val1 = %02x, val2 = %02x, val3 = %02x.\n",
Jeff Garzikd61780c2005-10-30 15:01:51 -0800557 val1, val2, val3);
558
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800560
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561#if 0
562 if (irqs <= 0) {
563 printk(KERN_ERR "sx%d: Can't find IRQ for specialix IO8+ board at 0x%03x.\n",
564 board_No(bp), bp->base);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800565 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 func_exit();
567 return 1;
568 }
569#endif
570 printk (KERN_INFO "Started with irq=%d, but now have irq=%d.\n", bp->irq, irqs);
571 if (irqs > 0)
572 bp->irq = irqs;
573#endif
574 /* Reset CD186x again */
575 if (!sx_init_CD186x(bp)) {
Jeff Garzikd61780c2005-10-30 15:01:51 -0800576 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577 func_exit();
Jeff Garzikd61780c2005-10-30 15:01:51 -0800578 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 }
580
581 sx_request_io_range(bp);
582 bp->flags |= SX_BOARD_PRESENT;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800583
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584 /* Chip revcode pkgtype
585 GFRCR SRCR bit 7
586 CD180 rev B 0x81 0
587 CD180 rev C 0x82 0
588 CD1864 rev A 0x82 1
Jeff Garzikd61780c2005-10-30 15:01:51 -0800589 CD1865 rev A 0x83 1 -- Do not use!!! Does not work.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590 CD1865 rev B 0x84 1
591 -- Thanks to Gwen Wang, Cirrus Logic.
592 */
593
594 switch (sx_in_off(bp, CD186x_GFRCR)) {
595 case 0x82:chip = 1864;rev='A';break;
596 case 0x83:chip = 1865;rev='A';break;
597 case 0x84:chip = 1865;rev='B';break;
598 case 0x85:chip = 1865;rev='C';break; /* Does not exist at this time */
599 default:chip=-1;rev='x';
600 }
601
602 dprintk (SX_DEBUG_INIT, " GFCR = 0x%02x\n", sx_in_off(bp, CD186x_GFRCR) );
603
604#ifdef SPECIALIX_TIMER
605 init_timer (&missed_irq_timer);
606 missed_irq_timer.function = missed_irq;
607 missed_irq_timer.data = (unsigned long) bp;
608 missed_irq_timer.expires = jiffies + sx_poll;
609 add_timer (&missed_irq_timer);
610#endif
611
612 printk(KERN_INFO"sx%d: specialix IO8+ board detected at 0x%03x, IRQ %d, CD%d Rev. %c.\n",
613 board_No(bp),
614 bp->base, bp->irq,
615 chip, rev);
616
617 func_exit();
618 return 0;
619}
620
Jeff Garzikd61780c2005-10-30 15:01:51 -0800621/*
622 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623 * Interrupt processing routines.
624 * */
625
626static inline void sx_mark_event(struct specialix_port * port, int event)
627{
628 func_enter();
629
630 set_bit(event, &port->event);
631 schedule_work(&port->tqueue);
632
633 func_exit();
634}
635
636
637static inline struct specialix_port * sx_get_port(struct specialix_board * bp,
638 unsigned char const * what)
639{
640 unsigned char channel;
641 struct specialix_port * port = NULL;
642
643 channel = sx_in(bp, CD186x_GICR) >> GICR_CHAN_OFF;
644 dprintk (SX_DEBUG_CHAN, "channel: %d\n", channel);
645 if (channel < CD186x_NCH) {
646 port = &sx_port[board_No(bp) * SX_NPORT + channel];
647 dprintk (SX_DEBUG_CHAN, "port: %d %p flags: 0x%x\n",board_No(bp) * SX_NPORT + channel, port, port->flags & ASYNC_INITIALIZED);
648
649 if (port->flags & ASYNC_INITIALIZED) {
650 dprintk (SX_DEBUG_CHAN, "port: %d %p\n", channel, port);
651 func_exit();
652 return port;
653 }
654 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800655 printk(KERN_INFO "sx%d: %s interrupt from invalid port %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656 board_No(bp), what, channel);
657 return NULL;
658}
659
660
661static inline void sx_receive_exc(struct specialix_board * bp)
662{
663 struct specialix_port *port;
664 struct tty_struct *tty;
665 unsigned char status;
Alan Cox33f0f882006-01-09 20:54:13 -0800666 unsigned char ch, flag;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667
668 func_enter();
669
670 port = sx_get_port(bp, "Receive");
671 if (!port) {
672 dprintk (SX_DEBUG_RX, "Hmm, couldn't find port.\n");
673 func_exit();
674 return;
675 }
676 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800677
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678 status = sx_in(bp, CD186x_RCSR);
679
680 dprintk (SX_DEBUG_RX, "status: 0x%x\n", status);
681 if (status & RCSR_OE) {
682 port->overrun++;
683 dprintk(SX_DEBUG_FIFO, "sx%d: port %d: Overrun. Total %ld overruns.\n",
684 board_No(bp), port_No(port), port->overrun);
685 }
686 status &= port->mark_mask;
687
688 /* This flip buffer check needs to be below the reading of the
689 status register to reset the chip's IRQ.... */
Alan Cox33f0f882006-01-09 20:54:13 -0800690 if (tty_buffer_request_room(tty, 1) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691 dprintk(SX_DEBUG_FIFO, "sx%d: port %d: Working around flip buffer overflow.\n",
692 board_No(bp), port_No(port));
693 func_exit();
694 return;
695 }
696
697 ch = sx_in(bp, CD186x_RDR);
698 if (!status) {
699 func_exit();
700 return;
701 }
702 if (status & RCSR_TOUT) {
Jeff Garzikd61780c2005-10-30 15:01:51 -0800703 printk(KERN_INFO "sx%d: port %d: Receiver timeout. Hardware problems ?\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704 board_No(bp), port_No(port));
705 func_exit();
706 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800707
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708 } else if (status & RCSR_BREAK) {
709 dprintk(SX_DEBUG_RX, "sx%d: port %d: Handling break...\n",
710 board_No(bp), port_No(port));
Alan Cox33f0f882006-01-09 20:54:13 -0800711 flag = TTY_BREAK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712 if (port->flags & ASYNC_SAK)
713 do_SAK(tty);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800714
715 } else if (status & RCSR_PE)
Alan Cox33f0f882006-01-09 20:54:13 -0800716 flag = TTY_PARITY;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800717
718 else if (status & RCSR_FE)
Alan Cox33f0f882006-01-09 20:54:13 -0800719 flag = TTY_FRAME;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800720
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721 else if (status & RCSR_OE)
Alan Cox33f0f882006-01-09 20:54:13 -0800722 flag = TTY_OVERRUN;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800723
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 else
Alan Cox33f0f882006-01-09 20:54:13 -0800725 flag = TTY_NORMAL;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800726
Alan Cox33f0f882006-01-09 20:54:13 -0800727 if(tty_insert_flip_char(tty, ch, flag))
728 tty_flip_buffer_push(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729 func_exit();
730}
731
732
733static inline void sx_receive(struct specialix_board * bp)
734{
735 struct specialix_port *port;
736 struct tty_struct *tty;
737 unsigned char count;
738
739 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -0800740
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741 if (!(port = sx_get_port(bp, "Receive"))) {
742 dprintk (SX_DEBUG_RX, "Hmm, couldn't find port.\n");
743 func_exit();
744 return;
745 }
746 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800747
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748 count = sx_in(bp, CD186x_RDCR);
749 dprintk (SX_DEBUG_RX, "port: %p: count: %d\n", port, count);
750 port->hits[count > 8 ? 9 : count]++;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800751
Alan Cox33f0f882006-01-09 20:54:13 -0800752 tty_buffer_request_room(tty, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753
Alan Cox33f0f882006-01-09 20:54:13 -0800754 while (count--)
755 tty_insert_flip_char(tty, sx_in(bp, CD186x_RDR), TTY_NORMAL);
756 tty_flip_buffer_push(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757 func_exit();
758}
759
760
761static inline void sx_transmit(struct specialix_board * bp)
762{
763 struct specialix_port *port;
764 struct tty_struct *tty;
765 unsigned char count;
766
767 func_enter();
768 if (!(port = sx_get_port(bp, "Transmit"))) {
769 func_exit();
770 return;
771 }
772 dprintk (SX_DEBUG_TX, "port: %p\n", port);
773 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800774
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 if (port->IER & IER_TXEMPTY) {
776 /* FIFO drained */
777 sx_out(bp, CD186x_CAR, port_No(port));
778 port->IER &= ~IER_TXEMPTY;
779 sx_out(bp, CD186x_IER, port->IER);
780 func_exit();
781 return;
782 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800783
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784 if ((port->xmit_cnt <= 0 && !port->break_length)
785 || tty->stopped || tty->hw_stopped) {
786 sx_out(bp, CD186x_CAR, port_No(port));
787 port->IER &= ~IER_TXRDY;
788 sx_out(bp, CD186x_IER, port->IER);
789 func_exit();
790 return;
791 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800792
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 if (port->break_length) {
794 if (port->break_length > 0) {
795 if (port->COR2 & COR2_ETC) {
796 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
797 sx_out(bp, CD186x_TDR, CD186x_C_SBRK);
798 port->COR2 &= ~COR2_ETC;
799 }
800 count = min_t(int, port->break_length, 0xff);
801 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
802 sx_out(bp, CD186x_TDR, CD186x_C_DELAY);
803 sx_out(bp, CD186x_TDR, count);
804 if (!(port->break_length -= count))
805 port->break_length--;
806 } else {
807 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
808 sx_out(bp, CD186x_TDR, CD186x_C_EBRK);
809 sx_out(bp, CD186x_COR2, port->COR2);
810 sx_wait_CCR(bp);
811 sx_out(bp, CD186x_CCR, CCR_CORCHG2);
812 port->break_length = 0;
813 }
814
815 func_exit();
816 return;
817 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800818
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819 count = CD186x_NFIFO;
820 do {
821 sx_out(bp, CD186x_TDR, port->xmit_buf[port->xmit_tail++]);
822 port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1);
823 if (--port->xmit_cnt <= 0)
824 break;
825 } while (--count > 0);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800826
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827 if (port->xmit_cnt <= 0) {
828 sx_out(bp, CD186x_CAR, port_No(port));
829 port->IER &= ~IER_TXRDY;
830 sx_out(bp, CD186x_IER, port->IER);
831 }
832 if (port->xmit_cnt <= port->wakeup_chars)
833 sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);
834
835 func_exit();
836}
837
838
839static inline void sx_check_modem(struct specialix_board * bp)
840{
841 struct specialix_port *port;
842 struct tty_struct *tty;
843 unsigned char mcr;
844 int msvr_cd;
845
846 dprintk (SX_DEBUG_SIGNALS, "Modem intr. ");
847 if (!(port = sx_get_port(bp, "Modem")))
848 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800849
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800851
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852 mcr = sx_in(bp, CD186x_MCR);
853 printk ("mcr = %02x.\n", mcr);
854
855 if ((mcr & MCR_CDCHG)) {
856 dprintk (SX_DEBUG_SIGNALS, "CD just changed... ");
857 msvr_cd = sx_in(bp, CD186x_MSVR) & MSVR_CD;
858 if (msvr_cd) {
859 dprintk (SX_DEBUG_SIGNALS, "Waking up guys in open.\n");
860 wake_up_interruptible(&port->open_wait);
861 } else {
862 dprintk (SX_DEBUG_SIGNALS, "Sending HUP.\n");
863 schedule_work(&port->tqueue_hangup);
864 }
865 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800866
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
868 if (mcr & MCR_CTSCHG) {
869 if (sx_in(bp, CD186x_MSVR) & MSVR_CTS) {
870 tty->hw_stopped = 0;
871 port->IER |= IER_TXRDY;
872 if (port->xmit_cnt <= port->wakeup_chars)
873 sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);
874 } else {
875 tty->hw_stopped = 1;
876 port->IER &= ~IER_TXRDY;
877 }
878 sx_out(bp, CD186x_IER, port->IER);
879 }
880 if (mcr & MCR_DSSXHG) {
881 if (sx_in(bp, CD186x_MSVR) & MSVR_DSR) {
882 tty->hw_stopped = 0;
883 port->IER |= IER_TXRDY;
884 if (port->xmit_cnt <= port->wakeup_chars)
885 sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);
886 } else {
887 tty->hw_stopped = 1;
888 port->IER &= ~IER_TXRDY;
889 }
890 sx_out(bp, CD186x_IER, port->IER);
891 }
892#endif /* SPECIALIX_BRAIN_DAMAGED_CTS */
Jeff Garzikd61780c2005-10-30 15:01:51 -0800893
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894 /* Clear change bits */
895 sx_out(bp, CD186x_MCR, 0);
896}
897
898
899/* The main interrupt processing routine */
900static irqreturn_t sx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
901{
902 unsigned char status;
903 unsigned char ack;
904 struct specialix_board *bp;
905 unsigned long loop = 0;
906 int saved_reg;
907 unsigned long flags;
908
909 func_enter();
910
911 bp = dev_id;
912 spin_lock_irqsave(&bp->lock, flags);
913
914 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);
915 if (!bp || !(bp->flags & SX_BOARD_ACTIVE)) {
916 dprintk (SX_DEBUG_IRQ, "sx: False interrupt. irq %d.\n", irq);
917 spin_unlock_irqrestore(&bp->lock, flags);
918 func_exit();
919 return IRQ_NONE;
920 }
921
922 saved_reg = bp->reg;
923
924 while ((++loop < 16) && (status = (sx_in(bp, CD186x_SRSR) &
925 (SRSR_RREQint |
926 SRSR_TREQint |
Jeff Garzikd61780c2005-10-30 15:01:51 -0800927 SRSR_MREQint)))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700928 if (status & SRSR_RREQint) {
929 ack = sx_in(bp, CD186x_RRAR);
930
931 if (ack == (SX_ID | GIVR_IT_RCV))
932 sx_receive(bp);
933 else if (ack == (SX_ID | GIVR_IT_REXC))
934 sx_receive_exc(bp);
935 else
936 printk(KERN_ERR "sx%d: status: 0x%x Bad receive ack 0x%02x.\n",
937 board_No(bp), status, ack);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800938
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939 } else if (status & SRSR_TREQint) {
940 ack = sx_in(bp, CD186x_TRAR);
941
942 if (ack == (SX_ID | GIVR_IT_TX))
943 sx_transmit(bp);
944 else
945 printk(KERN_ERR "sx%d: status: 0x%x Bad transmit ack 0x%02x. port: %d\n",
946 board_No(bp), status, ack, port_No (sx_get_port (bp, "Int")));
947 } else if (status & SRSR_MREQint) {
948 ack = sx_in(bp, CD186x_MRAR);
949
Jeff Garzikd61780c2005-10-30 15:01:51 -0800950 if (ack == (SX_ID | GIVR_IT_MODEM))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951 sx_check_modem(bp);
952 else
953 printk(KERN_ERR "sx%d: status: 0x%x Bad modem ack 0x%02x.\n",
954 board_No(bp), status, ack);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800955
956 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957
958 sx_out(bp, CD186x_EOIR, 0); /* Mark end of interrupt */
959 }
960 bp->reg = saved_reg;
961 outb (bp->reg, bp->base + SX_ADDR_REG);
962 spin_unlock_irqrestore(&bp->lock, flags);
963 func_exit();
964 return IRQ_HANDLED;
965}
966
967
968/*
969 * Routines for open & close processing.
970 */
971
972static void turn_ints_off (struct specialix_board *bp)
973{
974 unsigned long flags;
975
976 func_enter();
977 if (bp->flags & SX_BOARD_IS_PCI) {
978 /* This was intended for enabeling the interrupt on the
979 * PCI card. However it seems that it's already enabled
980 * and as PCI interrupts can be shared, there is no real
981 * reason to have to turn it off. */
982 }
983
984 spin_lock_irqsave(&bp->lock, flags);
985 (void) sx_in_off (bp, 0); /* Turn off interrupts. */
986 spin_unlock_irqrestore(&bp->lock, flags);
987
988 func_exit();
989}
990
991static void turn_ints_on (struct specialix_board *bp)
992{
993 unsigned long flags;
994
995 func_enter();
996
997 if (bp->flags & SX_BOARD_IS_PCI) {
998 /* play with the PCI chip. See comment above. */
999 }
1000 spin_lock_irqsave(&bp->lock, flags);
1001 (void) sx_in (bp, 0); /* Turn ON interrupts. */
1002 spin_unlock_irqrestore(&bp->lock, flags);
1003
1004 func_exit();
1005}
1006
1007
1008/* Called with disabled interrupts */
1009static inline int sx_setup_board(struct specialix_board * bp)
1010{
1011 int error;
1012
Jeff Garzikd61780c2005-10-30 15:01:51 -08001013 if (bp->flags & SX_BOARD_ACTIVE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001014 return 0;
1015
1016 if (bp->flags & SX_BOARD_IS_PCI)
Thomas Gleixner0f2ed4c2006-07-01 19:29:33 -07001017 error = request_irq(bp->irq, sx_interrupt, IRQF_DISABLED | IRQF_SHARED, "specialix IO8+", bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018 else
Thomas Gleixner0f2ed4c2006-07-01 19:29:33 -07001019 error = request_irq(bp->irq, sx_interrupt, IRQF_DISABLED, "specialix IO8+", bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001020
Jeff Garzikd61780c2005-10-30 15:01:51 -08001021 if (error)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022 return error;
1023
1024 turn_ints_on (bp);
1025 bp->flags |= SX_BOARD_ACTIVE;
1026
1027 return 0;
1028}
1029
1030
1031/* Called with disabled interrupts */
1032static inline void sx_shutdown_board(struct specialix_board *bp)
1033{
1034 func_enter();
1035
1036 if (!(bp->flags & SX_BOARD_ACTIVE)) {
1037 func_exit();
1038 return;
1039 }
1040
1041 bp->flags &= ~SX_BOARD_ACTIVE;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001042
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043 dprintk (SX_DEBUG_IRQ, "Freeing IRQ%d for board %d.\n",
1044 bp->irq, board_No (bp));
1045 free_irq(bp->irq, bp);
1046
1047 turn_ints_off (bp);
1048
1049
1050 func_exit();
1051}
1052
1053
1054/*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001055 * Setting up port characteristics.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056 * Must be called with disabled interrupts
1057 */
1058static void sx_change_speed(struct specialix_board *bp, struct specialix_port *port)
1059{
1060 struct tty_struct *tty;
1061 unsigned long baud;
1062 long tmp;
1063 unsigned char cor1 = 0, cor3 = 0;
1064 unsigned char mcor1 = 0, mcor2 = 0;
1065 static unsigned long again;
1066 unsigned long flags;
1067
1068 func_enter();
1069
1070 if (!(tty = port->tty) || !tty->termios) {
1071 func_exit();
1072 return;
1073 }
1074
1075 port->IER = 0;
1076 port->COR2 = 0;
1077 /* Select port on the board */
1078 spin_lock_irqsave(&bp->lock, flags);
1079 sx_out(bp, CD186x_CAR, port_No(port));
1080
1081 /* The Specialix board doens't implement the RTS lines.
1082 They are used to set the IRQ level. Don't touch them. */
1083 if (SX_CRTSCTS(tty))
1084 port->MSVR = MSVR_DTR | (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
1085 else
1086 port->MSVR = (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
1087 spin_unlock_irqrestore(&bp->lock, flags);
1088 dprintk (SX_DEBUG_TERMIOS, "sx: got MSVR=%02x.\n", port->MSVR);
Alan Cox67cc0162006-09-29 02:01:39 -07001089 baud = tty_get_baud_rate(tty);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001090
Alan Cox67cc0162006-09-29 02:01:39 -07001091 if (baud == 38400) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092 if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
1093 baud ++;
1094 if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
1095 baud += 2;
1096 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001097
Alan Cox67cc0162006-09-29 02:01:39 -07001098 if (!baud) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001099 /* Drop DTR & exit */
1100 dprintk (SX_DEBUG_TERMIOS, "Dropping DTR... Hmm....\n");
1101 if (!SX_CRTSCTS (tty)) {
1102 port -> MSVR &= ~ MSVR_DTR;
1103 spin_lock_irqsave(&bp->lock, flags);
1104 sx_out(bp, CD186x_MSVR, port->MSVR );
1105 spin_unlock_irqrestore(&bp->lock, flags);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001106 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001107 else
1108 dprintk (SX_DEBUG_TERMIOS, "Can't drop DTR: no DTR.\n");
1109 return;
1110 } else {
1111 /* Set DTR on */
1112 if (!SX_CRTSCTS (tty)) {
1113 port ->MSVR |= MSVR_DTR;
1114 }
1115 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001116
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117 /*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001118 * Now we must calculate some speed depended things
Linus Torvalds1da177e2005-04-16 15:20:36 -07001119 */
1120
1121 /* Set baud rate for port */
1122 tmp = port->custom_divisor ;
1123 if ( tmp )
1124 printk (KERN_INFO "sx%d: Using custom baud rate divisor %ld. \n"
1125 "This is an untested option, please be carefull.\n",
1126 port_No (port), tmp);
1127 else
Alan Cox67cc0162006-09-29 02:01:39 -07001128 tmp = (((SX_OSCFREQ + baud/2) / baud +
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129 CD186x_TPC/2) / CD186x_TPC);
1130
Jeff Garzikd61780c2005-10-30 15:01:51 -08001131 if ((tmp < 0x10) && time_before(again, jiffies)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132 again = jiffies + HZ * 60;
1133 /* Page 48 of version 2.0 of the CL-CD1865 databook */
1134 if (tmp >= 12) {
1135 printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
1136 "Performance degradation is possible.\n"
1137 "Read specialix.txt for more info.\n",
1138 port_No (port), tmp);
1139 } else {
1140 printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
1141 "Warning: overstressing Cirrus chip. "
1142 "This might not work.\n"
Jeff Garzikd61780c2005-10-30 15:01:51 -08001143 "Read specialix.txt for more info.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144 port_No (port), tmp);
1145 }
1146 }
1147 spin_lock_irqsave(&bp->lock, flags);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001148 sx_out(bp, CD186x_RBPRH, (tmp >> 8) & 0xff);
1149 sx_out(bp, CD186x_TBPRH, (tmp >> 8) & 0xff);
1150 sx_out(bp, CD186x_RBPRL, tmp & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151 sx_out(bp, CD186x_TBPRL, tmp & 0xff);
1152 spin_unlock_irqrestore(&bp->lock, flags);
1153 if (port->custom_divisor) {
1154 baud = (SX_OSCFREQ + port->custom_divisor/2) / port->custom_divisor;
1155 baud = ( baud + 5 ) / 10;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001156 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001157 baud = (baud_table[baud] + 5) / 10; /* Estimated CPS */
1158
1159 /* Two timer ticks seems enough to wakeup something like SLIP driver */
Jeff Garzikd61780c2005-10-30 15:01:51 -08001160 tmp = ((baud + HZ/2) / HZ) * 2 - CD186x_NFIFO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161 port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ?
1162 SERIAL_XMIT_SIZE - 1 : tmp);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001163
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 /* Receiver timeout will be transmission time for 1.5 chars */
1165 tmp = (SPECIALIX_TPS + SPECIALIX_TPS/2 + baud/2) / baud;
1166 tmp = (tmp > 0xff) ? 0xff : tmp;
1167 spin_lock_irqsave(&bp->lock, flags);
1168 sx_out(bp, CD186x_RTPR, tmp);
1169 spin_unlock_irqrestore(&bp->lock, flags);
1170 switch (C_CSIZE(tty)) {
1171 case CS5:
1172 cor1 |= COR1_5BITS;
1173 break;
1174 case CS6:
1175 cor1 |= COR1_6BITS;
1176 break;
1177 case CS7:
1178 cor1 |= COR1_7BITS;
1179 break;
1180 case CS8:
1181 cor1 |= COR1_8BITS;
1182 break;
1183 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001184
1185 if (C_CSTOPB(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001186 cor1 |= COR1_2SB;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001187
Linus Torvalds1da177e2005-04-16 15:20:36 -07001188 cor1 |= COR1_IGNORE;
1189 if (C_PARENB(tty)) {
1190 cor1 |= COR1_NORMPAR;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001191 if (C_PARODD(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001192 cor1 |= COR1_ODDP;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001193 if (I_INPCK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001194 cor1 &= ~COR1_IGNORE;
1195 }
1196 /* Set marking of some errors */
1197 port->mark_mask = RCSR_OE | RCSR_TOUT;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001198 if (I_INPCK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199 port->mark_mask |= RCSR_FE | RCSR_PE;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001200 if (I_BRKINT(tty) || I_PARMRK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001201 port->mark_mask |= RCSR_BREAK;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001202 if (I_IGNPAR(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001203 port->mark_mask &= ~(RCSR_FE | RCSR_PE);
1204 if (I_IGNBRK(tty)) {
1205 port->mark_mask &= ~RCSR_BREAK;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001206 if (I_IGNPAR(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207 /* Real raw mode. Ignore all */
1208 port->mark_mask &= ~RCSR_OE;
1209 }
1210 /* Enable Hardware Flow Control */
1211 if (C_CRTSCTS(tty)) {
1212#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
1213 port->IER |= IER_DSR | IER_CTS;
1214 mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD;
1215 mcor2 |= MCOR2_DSROD | MCOR2_CTSOD;
1216 spin_lock_irqsave(&bp->lock, flags);
1217 tty->hw_stopped = !(sx_in(bp, CD186x_MSVR) & (MSVR_CTS|MSVR_DSR));
1218 spin_unlock_irqrestore(&bp->lock, flags);
1219#else
Jeff Garzikd61780c2005-10-30 15:01:51 -08001220 port->COR2 |= COR2_CTSAE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001221#endif
1222 }
1223 /* Enable Software Flow Control. FIXME: I'm not sure about this */
1224 /* Some people reported that it works, but I still doubt it */
1225 if (I_IXON(tty)) {
1226 port->COR2 |= COR2_TXIBE;
1227 cor3 |= (COR3_FCT | COR3_SCDE);
1228 if (I_IXANY(tty))
1229 port->COR2 |= COR2_IXM;
1230 spin_lock_irqsave(&bp->lock, flags);
1231 sx_out(bp, CD186x_SCHR1, START_CHAR(tty));
1232 sx_out(bp, CD186x_SCHR2, STOP_CHAR(tty));
1233 sx_out(bp, CD186x_SCHR3, START_CHAR(tty));
1234 sx_out(bp, CD186x_SCHR4, STOP_CHAR(tty));
1235 spin_unlock_irqrestore(&bp->lock, flags);
1236 }
1237 if (!C_CLOCAL(tty)) {
1238 /* Enable CD check */
1239 port->IER |= IER_CD;
1240 mcor1 |= MCOR1_CDZD;
1241 mcor2 |= MCOR2_CDOD;
1242 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001243
1244 if (C_CREAD(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001245 /* Enable receiver */
1246 port->IER |= IER_RXD;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001247
Linus Torvalds1da177e2005-04-16 15:20:36 -07001248 /* Set input FIFO size (1-8 bytes) */
1249 cor3 |= sx_rxfifo;
1250 /* Setting up CD186x channel registers */
1251 spin_lock_irqsave(&bp->lock, flags);
1252 sx_out(bp, CD186x_COR1, cor1);
1253 sx_out(bp, CD186x_COR2, port->COR2);
1254 sx_out(bp, CD186x_COR3, cor3);
1255 spin_unlock_irqrestore(&bp->lock, flags);
1256 /* Make CD186x know about registers change */
1257 sx_wait_CCR(bp);
1258 spin_lock_irqsave(&bp->lock, flags);
1259 sx_out(bp, CD186x_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3);
1260 /* Setting up modem option registers */
1261 dprintk (SX_DEBUG_TERMIOS, "Mcor1 = %02x, mcor2 = %02x.\n", mcor1, mcor2);
1262 sx_out(bp, CD186x_MCOR1, mcor1);
1263 sx_out(bp, CD186x_MCOR2, mcor2);
1264 spin_unlock_irqrestore(&bp->lock, flags);
1265 /* Enable CD186x transmitter & receiver */
1266 sx_wait_CCR(bp);
1267 spin_lock_irqsave(&bp->lock, flags);
1268 sx_out(bp, CD186x_CCR, CCR_TXEN | CCR_RXEN);
1269 /* Enable interrupts */
1270 sx_out(bp, CD186x_IER, port->IER);
1271 /* And finally set the modem lines... */
1272 sx_out(bp, CD186x_MSVR, port->MSVR);
1273 spin_unlock_irqrestore(&bp->lock, flags);
1274
1275 func_exit();
1276}
1277
1278
1279/* Must be called with interrupts enabled */
1280static int sx_setup_port(struct specialix_board *bp, struct specialix_port *port)
1281{
1282 unsigned long flags;
1283
1284 func_enter();
1285
1286 if (port->flags & ASYNC_INITIALIZED) {
1287 func_exit();
1288 return 0;
1289 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001290
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291 if (!port->xmit_buf) {
1292 /* We may sleep in get_zeroed_page() */
1293 unsigned long tmp;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001294
Linus Torvalds1da177e2005-04-16 15:20:36 -07001295 if (!(tmp = get_zeroed_page(GFP_KERNEL))) {
1296 func_exit();
1297 return -ENOMEM;
1298 }
1299
1300 if (port->xmit_buf) {
1301 free_page(tmp);
1302 func_exit();
1303 return -ERESTARTSYS;
1304 }
1305 port->xmit_buf = (unsigned char *) tmp;
1306 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001307
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308 spin_lock_irqsave(&port->lock, flags);
1309
Jeff Garzikd61780c2005-10-30 15:01:51 -08001310 if (port->tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001311 clear_bit(TTY_IO_ERROR, &port->tty->flags);
1312
1313 port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
1314 sx_change_speed(bp, port);
1315 port->flags |= ASYNC_INITIALIZED;
1316
1317 spin_unlock_irqrestore(&port->lock, flags);
1318
Jeff Garzikd61780c2005-10-30 15:01:51 -08001319
Linus Torvalds1da177e2005-04-16 15:20:36 -07001320 func_exit();
1321 return 0;
1322}
1323
1324
1325/* Must be called with interrupts disabled */
1326static void sx_shutdown_port(struct specialix_board *bp, struct specialix_port *port)
1327{
1328 struct tty_struct *tty;
1329 int i;
1330 unsigned long flags;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001331
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332 func_enter();
1333
1334 if (!(port->flags & ASYNC_INITIALIZED)) {
1335 func_exit();
1336 return;
1337 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001338
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339 if (sx_debug & SX_DEBUG_FIFO) {
1340 dprintk(SX_DEBUG_FIFO, "sx%d: port %d: %ld overruns, FIFO hits [ ",
1341 board_No(bp), port_No(port), port->overrun);
1342 for (i = 0; i < 10; i++) {
1343 dprintk(SX_DEBUG_FIFO, "%ld ", port->hits[i]);
1344 }
1345 dprintk(SX_DEBUG_FIFO, "].\n");
1346 }
1347
1348 if (port->xmit_buf) {
1349 free_page((unsigned long) port->xmit_buf);
1350 port->xmit_buf = NULL;
1351 }
1352
1353 /* Select port */
1354 spin_lock_irqsave(&bp->lock, flags);
1355 sx_out(bp, CD186x_CAR, port_No(port));
1356
1357 if (!(tty = port->tty) || C_HUPCL(tty)) {
1358 /* Drop DTR */
1359 sx_out(bp, CD186x_MSVDTR, 0);
1360 }
1361 spin_unlock_irqrestore(&bp->lock, flags);
1362 /* Reset port */
1363 sx_wait_CCR(bp);
1364 spin_lock_irqsave(&bp->lock, flags);
1365 sx_out(bp, CD186x_CCR, CCR_SOFTRESET);
1366 /* Disable all interrupts from this port */
1367 port->IER = 0;
1368 sx_out(bp, CD186x_IER, port->IER);
1369 spin_unlock_irqrestore(&bp->lock, flags);
1370 if (tty)
1371 set_bit(TTY_IO_ERROR, &tty->flags);
1372 port->flags &= ~ASYNC_INITIALIZED;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001373
1374 if (!bp->count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001375 sx_shutdown_board(bp);
1376 func_exit();
1377}
1378
Jeff Garzikd61780c2005-10-30 15:01:51 -08001379
Linus Torvalds1da177e2005-04-16 15:20:36 -07001380static int block_til_ready(struct tty_struct *tty, struct file * filp,
1381 struct specialix_port *port)
1382{
1383 DECLARE_WAITQUEUE(wait, current);
1384 struct specialix_board *bp = port_Board(port);
1385 int retval;
1386 int do_clocal = 0;
1387 int CD;
1388 unsigned long flags;
1389
1390 func_enter();
1391
1392 /*
1393 * If the device is in the middle of being closed, then block
1394 * until it's done, and then try again.
1395 */
1396 if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {
1397 interruptible_sleep_on(&port->close_wait);
1398 if (port->flags & ASYNC_HUP_NOTIFY) {
1399 func_exit();
1400 return -EAGAIN;
1401 } else {
1402 func_exit();
1403 return -ERESTARTSYS;
1404 }
1405 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001406
Linus Torvalds1da177e2005-04-16 15:20:36 -07001407 /*
1408 * If non-blocking mode is set, or the port is not enabled,
1409 * then make the check up front and then exit.
1410 */
1411 if ((filp->f_flags & O_NONBLOCK) ||
1412 (tty->flags & (1 << TTY_IO_ERROR))) {
1413 port->flags |= ASYNC_NORMAL_ACTIVE;
1414 func_exit();
1415 return 0;
1416 }
1417
1418 if (C_CLOCAL(tty))
1419 do_clocal = 1;
1420
1421 /*
1422 * Block waiting for the carrier detect and the line to become
1423 * free (i.e., not in use by the callout). While we are in
1424 * this loop, info->count is dropped by one, so that
1425 * rs_close() knows when to free things. We restore it upon
1426 * exit, either normal or abnormal.
1427 */
1428 retval = 0;
1429 add_wait_queue(&port->open_wait, &wait);
1430 spin_lock_irqsave(&port->lock, flags);
1431 if (!tty_hung_up_p(filp)) {
1432 port->count--;
1433 }
1434 spin_unlock_irqrestore(&port->lock, flags);
1435 port->blocked_open++;
1436 while (1) {
1437 spin_lock_irqsave(&bp->lock, flags);
1438 sx_out(bp, CD186x_CAR, port_No(port));
1439 CD = sx_in(bp, CD186x_MSVR) & MSVR_CD;
1440 if (SX_CRTSCTS (tty)) {
1441 /* Activate RTS */
1442 port->MSVR |= MSVR_DTR; /* WTF? */
1443 sx_out (bp, CD186x_MSVR, port->MSVR);
1444 } else {
1445 /* Activate DTR */
1446 port->MSVR |= MSVR_DTR;
1447 sx_out (bp, CD186x_MSVR, port->MSVR);
1448 }
1449 spin_unlock_irqrestore(&bp->lock, flags);
1450 set_current_state(TASK_INTERRUPTIBLE);
1451 if (tty_hung_up_p(filp) ||
1452 !(port->flags & ASYNC_INITIALIZED)) {
1453 if (port->flags & ASYNC_HUP_NOTIFY)
1454 retval = -EAGAIN;
1455 else
Jeff Garzikd61780c2005-10-30 15:01:51 -08001456 retval = -ERESTARTSYS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001457 break;
1458 }
1459 if (!(port->flags & ASYNC_CLOSING) &&
1460 (do_clocal || CD))
1461 break;
1462 if (signal_pending(current)) {
1463 retval = -ERESTARTSYS;
1464 break;
1465 }
1466 schedule();
1467 }
1468
1469 set_current_state(TASK_RUNNING);
1470 remove_wait_queue(&port->open_wait, &wait);
1471 spin_lock_irqsave(&port->lock, flags);
1472 if (!tty_hung_up_p(filp)) {
1473 port->count++;
1474 }
1475 port->blocked_open--;
1476 spin_unlock_irqrestore(&port->lock, flags);
1477 if (retval) {
1478 func_exit();
1479 return retval;
1480 }
1481
1482 port->flags |= ASYNC_NORMAL_ACTIVE;
1483 func_exit();
1484 return 0;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001485}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001486
1487
1488static int sx_open(struct tty_struct * tty, struct file * filp)
1489{
1490 int board;
1491 int error;
1492 struct specialix_port * port;
1493 struct specialix_board * bp;
1494 int i;
1495 unsigned long flags;
1496
1497 func_enter();
1498
1499 board = SX_BOARD(tty->index);
1500
1501 if (board >= SX_NBOARD || !(sx_board[board].flags & SX_BOARD_PRESENT)) {
1502 func_exit();
1503 return -ENODEV;
1504 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001505
Linus Torvalds1da177e2005-04-16 15:20:36 -07001506 bp = &sx_board[board];
1507 port = sx_port + board * SX_NPORT + SX_PORT(tty->index);
1508 port->overrun = 0;
1509 for (i = 0; i < 10; i++)
1510 port->hits[i]=0;
1511
1512 dprintk (SX_DEBUG_OPEN, "Board = %d, bp = %p, port = %p, portno = %d.\n",
1513 board, bp, port, SX_PORT(tty->index));
1514
1515 if (sx_paranoia_check(port, tty->name, "sx_open")) {
1516 func_enter();
1517 return -ENODEV;
1518 }
1519
1520 if ((error = sx_setup_board(bp))) {
1521 func_exit();
1522 return error;
1523 }
1524
1525 spin_lock_irqsave(&bp->lock, flags);
1526 port->count++;
1527 bp->count++;
1528 tty->driver_data = port;
1529 port->tty = tty;
1530 spin_unlock_irqrestore(&bp->lock, flags);
1531
1532 if ((error = sx_setup_port(bp, port))) {
1533 func_enter();
1534 return error;
1535 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001536
Linus Torvalds1da177e2005-04-16 15:20:36 -07001537 if ((error = block_til_ready(tty, filp, port))) {
1538 func_enter();
1539 return error;
1540 }
1541
1542 func_exit();
1543 return 0;
1544}
1545
1546
1547static void sx_close(struct tty_struct * tty, struct file * filp)
1548{
1549 struct specialix_port *port = (struct specialix_port *) tty->driver_data;
1550 struct specialix_board *bp;
1551 unsigned long flags;
1552 unsigned long timeout;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001553
Linus Torvalds1da177e2005-04-16 15:20:36 -07001554 func_enter();
1555 if (!port || sx_paranoia_check(port, tty->name, "close")) {
1556 func_exit();
1557 return;
1558 }
1559 spin_lock_irqsave(&port->lock, flags);
1560
1561 if (tty_hung_up_p(filp)) {
1562 spin_unlock_irqrestore(&port->lock, flags);
1563 func_exit();
1564 return;
1565 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001566
Linus Torvalds1da177e2005-04-16 15:20:36 -07001567 bp = port_Board(port);
1568 if ((tty->count == 1) && (port->count != 1)) {
1569 printk(KERN_ERR "sx%d: sx_close: bad port count;"
1570 " tty->count is 1, port count is %d\n",
1571 board_No(bp), port->count);
1572 port->count = 1;
1573 }
1574
1575 if (port->count > 1) {
1576 port->count--;
1577 bp->count--;
1578
1579 spin_unlock_irqrestore(&port->lock, flags);
1580
1581 func_exit();
1582 return;
1583 }
1584 port->flags |= ASYNC_CLOSING;
1585 /*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001586 * Now we wait for the transmit buffer to clear; and we notify
Linus Torvalds1da177e2005-04-16 15:20:36 -07001587 * the line discipline to only process XON/XOFF characters.
1588 */
1589 tty->closing = 1;
1590 spin_unlock_irqrestore(&port->lock, flags);
1591 dprintk (SX_DEBUG_OPEN, "Closing\n");
1592 if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) {
1593 tty_wait_until_sent(tty, port->closing_wait);
1594 }
1595 /*
1596 * At this point we stop accepting input. To do this, we
1597 * disable the receive line status interrupts, and tell the
1598 * interrupt driver to stop checking the data ready bit in the
1599 * line status register.
1600 */
1601 dprintk (SX_DEBUG_OPEN, "Closed\n");
1602 port->IER &= ~IER_RXD;
1603 if (port->flags & ASYNC_INITIALIZED) {
1604 port->IER &= ~IER_TXRDY;
1605 port->IER |= IER_TXEMPTY;
1606 spin_lock_irqsave(&bp->lock, flags);
1607 sx_out(bp, CD186x_CAR, port_No(port));
1608 sx_out(bp, CD186x_IER, port->IER);
1609 spin_unlock_irqrestore(&bp->lock, flags);
1610 /*
1611 * Before we drop DTR, make sure the UART transmitter
1612 * has completely drained; this is especially
1613 * important if there is a transmit FIFO!
1614 */
1615 timeout = jiffies+HZ;
1616 while(port->IER & IER_TXEMPTY) {
1617 set_current_state (TASK_INTERRUPTIBLE);
1618 msleep_interruptible(jiffies_to_msecs(port->timeout));
1619 if (time_after(jiffies, timeout)) {
1620 printk (KERN_INFO "Timeout waiting for close\n");
1621 break;
1622 }
1623 }
1624
1625 }
1626
1627 if (--bp->count < 0) {
1628 printk(KERN_ERR "sx%d: sx_shutdown_port: bad board count: %d port: %d\n",
1629 board_No(bp), bp->count, tty->index);
1630 bp->count = 0;
1631 }
1632 if (--port->count < 0) {
1633 printk(KERN_ERR "sx%d: sx_close: bad port count for tty%d: %d\n",
1634 board_No(bp), port_No(port), port->count);
1635 port->count = 0;
1636 }
1637
1638 sx_shutdown_port(bp, port);
1639 if (tty->driver->flush_buffer)
1640 tty->driver->flush_buffer(tty);
1641 tty_ldisc_flush(tty);
1642 spin_lock_irqsave(&port->lock, flags);
1643 tty->closing = 0;
1644 port->event = 0;
1645 port->tty = NULL;
1646 spin_unlock_irqrestore(&port->lock, flags);
1647 if (port->blocked_open) {
1648 if (port->close_delay) {
1649 msleep_interruptible(jiffies_to_msecs(port->close_delay));
1650 }
1651 wake_up_interruptible(&port->open_wait);
1652 }
1653 port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
1654 wake_up_interruptible(&port->close_wait);
1655
1656 func_exit();
1657}
1658
1659
Jeff Garzikd61780c2005-10-30 15:01:51 -08001660static int sx_write(struct tty_struct * tty,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001661 const unsigned char *buf, int count)
1662{
1663 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1664 struct specialix_board *bp;
1665 int c, total = 0;
1666 unsigned long flags;
1667
1668 func_enter();
1669 if (sx_paranoia_check(port, tty->name, "sx_write")) {
1670 func_exit();
1671 return 0;
1672 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001673
Linus Torvalds1da177e2005-04-16 15:20:36 -07001674 bp = port_Board(port);
1675
Jiri Slaby365e0222006-09-30 23:28:11 -07001676 if (!port->xmit_buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001677 func_exit();
1678 return 0;
1679 }
1680
1681 while (1) {
1682 spin_lock_irqsave(&port->lock, flags);
1683 c = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
1684 SERIAL_XMIT_SIZE - port->xmit_head));
1685 if (c <= 0) {
1686 spin_unlock_irqrestore(&port->lock, flags);
1687 break;
1688 }
1689 memcpy(port->xmit_buf + port->xmit_head, buf, c);
1690 port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
1691 port->xmit_cnt += c;
1692 spin_unlock_irqrestore(&port->lock, flags);
1693
1694 buf += c;
1695 count -= c;
1696 total += c;
1697 }
1698
1699 spin_lock_irqsave(&bp->lock, flags);
1700 if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
1701 !(port->IER & IER_TXRDY)) {
1702 port->IER |= IER_TXRDY;
1703 sx_out(bp, CD186x_CAR, port_No(port));
1704 sx_out(bp, CD186x_IER, port->IER);
1705 }
1706 spin_unlock_irqrestore(&bp->lock, flags);
1707 func_exit();
1708
1709 return total;
1710}
1711
1712
1713static void sx_put_char(struct tty_struct * tty, unsigned char ch)
1714{
1715 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1716 unsigned long flags;
1717 struct specialix_board * bp;
1718
1719 func_enter();
1720
1721 if (sx_paranoia_check(port, tty->name, "sx_put_char")) {
1722 func_exit();
1723 return;
1724 }
1725 dprintk (SX_DEBUG_TX, "check tty: %p %p\n", tty, port->xmit_buf);
Eric Sesterhenn326f28e92006-06-25 05:48:48 -07001726 if (!port->xmit_buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001727 func_exit();
1728 return;
1729 }
1730 bp = port_Board(port);
1731 spin_lock_irqsave(&port->lock, flags);
1732
1733 dprintk (SX_DEBUG_TX, "xmit_cnt: %d xmit_buf: %p\n", port->xmit_cnt, port->xmit_buf);
1734 if ((port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) || (!port->xmit_buf)) {
1735 spin_unlock_irqrestore(&port->lock, flags);
1736 dprintk (SX_DEBUG_TX, "Exit size\n");
1737 func_exit();
1738 return;
1739 }
1740 dprintk (SX_DEBUG_TX, "Handle xmit: %p %p\n", port, port->xmit_buf);
1741 port->xmit_buf[port->xmit_head++] = ch;
1742 port->xmit_head &= SERIAL_XMIT_SIZE - 1;
1743 port->xmit_cnt++;
1744 spin_unlock_irqrestore(&port->lock, flags);
1745
1746 func_exit();
1747}
1748
1749
1750static void sx_flush_chars(struct tty_struct * tty)
1751{
1752 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1753 unsigned long flags;
1754 struct specialix_board * bp = port_Board(port);
1755
1756 func_enter();
1757
1758 if (sx_paranoia_check(port, tty->name, "sx_flush_chars")) {
1759 func_exit();
1760 return;
1761 }
1762 if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
1763 !port->xmit_buf) {
1764 func_exit();
1765 return;
1766 }
1767 spin_lock_irqsave(&bp->lock, flags);
1768 port->IER |= IER_TXRDY;
1769 sx_out(port_Board(port), CD186x_CAR, port_No(port));
1770 sx_out(port_Board(port), CD186x_IER, port->IER);
1771 spin_unlock_irqrestore(&bp->lock, flags);
1772
1773 func_exit();
1774}
1775
1776
1777static int sx_write_room(struct tty_struct * tty)
1778{
1779 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1780 int ret;
1781
1782 func_enter();
1783
1784 if (sx_paranoia_check(port, tty->name, "sx_write_room")) {
1785 func_exit();
1786 return 0;
1787 }
1788
1789 ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;
1790 if (ret < 0)
1791 ret = 0;
1792
1793 func_exit();
1794 return ret;
1795}
1796
1797
1798static int sx_chars_in_buffer(struct tty_struct *tty)
1799{
1800 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1801
1802 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08001803
Linus Torvalds1da177e2005-04-16 15:20:36 -07001804 if (sx_paranoia_check(port, tty->name, "sx_chars_in_buffer")) {
1805 func_exit();
1806 return 0;
1807 }
1808 func_exit();
1809 return port->xmit_cnt;
1810}
1811
1812
1813static void sx_flush_buffer(struct tty_struct *tty)
1814{
1815 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1816 unsigned long flags;
1817 struct specialix_board * bp;
1818
1819 func_enter();
1820
1821 if (sx_paranoia_check(port, tty->name, "sx_flush_buffer")) {
1822 func_exit();
1823 return;
1824 }
1825
1826 bp = port_Board(port);
1827 spin_lock_irqsave(&port->lock, flags);
1828 port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
1829 spin_unlock_irqrestore(&port->lock, flags);
1830 tty_wakeup(tty);
1831
1832 func_exit();
1833}
1834
1835
1836static int sx_tiocmget(struct tty_struct *tty, struct file *file)
1837{
1838 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1839 struct specialix_board * bp;
1840 unsigned char status;
1841 unsigned int result;
1842 unsigned long flags;
1843
1844 func_enter();
1845
1846 if (sx_paranoia_check(port, tty->name, __FUNCTION__)) {
1847 func_exit();
1848 return -ENODEV;
1849 }
1850
1851 bp = port_Board(port);
1852 spin_lock_irqsave (&bp->lock, flags);
1853 sx_out(bp, CD186x_CAR, port_No(port));
1854 status = sx_in(bp, CD186x_MSVR);
1855 spin_unlock_irqrestore(&bp->lock, flags);
1856 dprintk (SX_DEBUG_INIT, "Got msvr[%d] = %02x, car = %d.\n",
1857 port_No(port), status, sx_in (bp, CD186x_CAR));
1858 dprintk (SX_DEBUG_INIT, "sx_port = %p, port = %p\n", sx_port, port);
1859 if (SX_CRTSCTS(port->tty)) {
Jeff Garzikd61780c2005-10-30 15:01:51 -08001860 result = /* (status & MSVR_RTS) ? */ TIOCM_DTR /* : 0) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001861 | ((status & MSVR_DTR) ? TIOCM_RTS : 0)
1862 | ((status & MSVR_CD) ? TIOCM_CAR : 0)
1863 |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */
1864 | ((status & MSVR_CTS) ? TIOCM_CTS : 0);
1865 } else {
Jeff Garzikd61780c2005-10-30 15:01:51 -08001866 result = /* (status & MSVR_RTS) ? */ TIOCM_RTS /* : 0) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001867 | ((status & MSVR_DTR) ? TIOCM_DTR : 0)
1868 | ((status & MSVR_CD) ? TIOCM_CAR : 0)
1869 |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */
1870 | ((status & MSVR_CTS) ? TIOCM_CTS : 0);
1871 }
1872
1873 func_exit();
1874
1875 return result;
1876}
1877
1878
1879static int sx_tiocmset(struct tty_struct *tty, struct file *file,
1880 unsigned int set, unsigned int clear)
1881{
1882 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1883 unsigned long flags;
1884 struct specialix_board *bp;
1885
1886 func_enter();
1887
1888 if (sx_paranoia_check(port, tty->name, __FUNCTION__)) {
1889 func_exit();
1890 return -ENODEV;
1891 }
1892
1893 bp = port_Board(port);
1894
1895 spin_lock_irqsave(&port->lock, flags);
1896 /* if (set & TIOCM_RTS)
1897 port->MSVR |= MSVR_RTS; */
1898 /* if (set & TIOCM_DTR)
1899 port->MSVR |= MSVR_DTR; */
1900
1901 if (SX_CRTSCTS(port->tty)) {
1902 if (set & TIOCM_RTS)
1903 port->MSVR |= MSVR_DTR;
1904 } else {
1905 if (set & TIOCM_DTR)
1906 port->MSVR |= MSVR_DTR;
1907 }
1908
1909 /* if (clear & TIOCM_RTS)
1910 port->MSVR &= ~MSVR_RTS; */
1911 /* if (clear & TIOCM_DTR)
1912 port->MSVR &= ~MSVR_DTR; */
1913 if (SX_CRTSCTS(port->tty)) {
1914 if (clear & TIOCM_RTS)
1915 port->MSVR &= ~MSVR_DTR;
1916 } else {
1917 if (clear & TIOCM_DTR)
1918 port->MSVR &= ~MSVR_DTR;
1919 }
1920 spin_lock_irqsave(&bp->lock, flags);
1921 sx_out(bp, CD186x_CAR, port_No(port));
1922 sx_out(bp, CD186x_MSVR, port->MSVR);
1923 spin_unlock_irqrestore(&bp->lock, flags);
1924 spin_unlock_irqrestore(&port->lock, flags);
1925 func_exit();
1926 return 0;
1927}
1928
1929
1930static inline void sx_send_break(struct specialix_port * port, unsigned long length)
1931{
1932 struct specialix_board *bp = port_Board(port);
1933 unsigned long flags;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001934
Linus Torvalds1da177e2005-04-16 15:20:36 -07001935 func_enter();
1936
1937 spin_lock_irqsave (&port->lock, flags);
1938 port->break_length = SPECIALIX_TPS / HZ * length;
1939 port->COR2 |= COR2_ETC;
1940 port->IER |= IER_TXRDY;
1941 spin_lock_irqsave(&bp->lock, flags);
1942 sx_out(bp, CD186x_CAR, port_No(port));
1943 sx_out(bp, CD186x_COR2, port->COR2);
1944 sx_out(bp, CD186x_IER, port->IER);
1945 spin_unlock_irqrestore(&bp->lock, flags);
1946 spin_unlock_irqrestore (&port->lock, flags);
1947 sx_wait_CCR(bp);
1948 spin_lock_irqsave(&bp->lock, flags);
1949 sx_out(bp, CD186x_CCR, CCR_CORCHG2);
1950 spin_unlock_irqrestore(&bp->lock, flags);
1951 sx_wait_CCR(bp);
1952
1953 func_exit();
1954}
1955
1956
1957static inline int sx_set_serial_info(struct specialix_port * port,
1958 struct serial_struct __user * newinfo)
1959{
1960 struct serial_struct tmp;
1961 struct specialix_board *bp = port_Board(port);
1962 int change_speed;
1963
1964 func_enter();
1965 /*
Jesper Juhle49332b2005-05-01 08:59:08 -07001966 if (!access_ok(VERIFY_READ, (void *) newinfo, sizeof(tmp))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001967 func_exit();
Jesper Juhle49332b2005-05-01 08:59:08 -07001968 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001969 }
1970 */
1971 if (copy_from_user(&tmp, newinfo, sizeof(tmp))) {
1972 func_enter();
1973 return -EFAULT;
1974 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001975
1976#if 0
Linus Torvalds1da177e2005-04-16 15:20:36 -07001977 if ((tmp.irq != bp->irq) ||
1978 (tmp.port != bp->base) ||
1979 (tmp.type != PORT_CIRRUS) ||
1980 (tmp.baud_base != (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC) ||
1981 (tmp.custom_divisor != 0) ||
1982 (tmp.xmit_fifo_size != CD186x_NFIFO) ||
1983 (tmp.flags & ~SPECIALIX_LEGAL_FLAGS)) {
1984 func_exit();
1985 return -EINVAL;
1986 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001987#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001988
1989 change_speed = ((port->flags & ASYNC_SPD_MASK) !=
1990 (tmp.flags & ASYNC_SPD_MASK));
1991 change_speed |= (tmp.custom_divisor != port->custom_divisor);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001992
Linus Torvalds1da177e2005-04-16 15:20:36 -07001993 if (!capable(CAP_SYS_ADMIN)) {
1994 if ((tmp.close_delay != port->close_delay) ||
1995 (tmp.closing_wait != port->closing_wait) ||
1996 ((tmp.flags & ~ASYNC_USR_MASK) !=
1997 (port->flags & ~ASYNC_USR_MASK))) {
1998 func_exit();
1999 return -EPERM;
2000 }
2001 port->flags = ((port->flags & ~ASYNC_USR_MASK) |
2002 (tmp.flags & ASYNC_USR_MASK));
2003 port->custom_divisor = tmp.custom_divisor;
2004 } else {
2005 port->flags = ((port->flags & ~ASYNC_FLAGS) |
2006 (tmp.flags & ASYNC_FLAGS));
2007 port->close_delay = tmp.close_delay;
2008 port->closing_wait = tmp.closing_wait;
2009 port->custom_divisor = tmp.custom_divisor;
2010 }
2011 if (change_speed) {
2012 sx_change_speed(bp, port);
2013 }
2014 func_exit();
2015 return 0;
2016}
2017
2018
2019static inline int sx_get_serial_info(struct specialix_port * port,
2020 struct serial_struct __user *retinfo)
2021{
2022 struct serial_struct tmp;
2023 struct specialix_board *bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002024
Linus Torvalds1da177e2005-04-16 15:20:36 -07002025 func_enter();
2026
2027 /*
Jesper Juhle49332b2005-05-01 08:59:08 -07002028 if (!access_ok(VERIFY_WRITE, (void *) retinfo, sizeof(tmp)))
2029 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002030 */
2031
2032 memset(&tmp, 0, sizeof(tmp));
2033 tmp.type = PORT_CIRRUS;
2034 tmp.line = port - sx_port;
2035 tmp.port = bp->base;
2036 tmp.irq = bp->irq;
2037 tmp.flags = port->flags;
2038 tmp.baud_base = (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC;
2039 tmp.close_delay = port->close_delay * HZ/100;
2040 tmp.closing_wait = port->closing_wait * HZ/100;
2041 tmp.custom_divisor = port->custom_divisor;
2042 tmp.xmit_fifo_size = CD186x_NFIFO;
2043 if (copy_to_user(retinfo, &tmp, sizeof(tmp))) {
2044 func_exit();
2045 return -EFAULT;
2046 }
2047
2048 func_exit();
2049 return 0;
2050}
2051
2052
Jeff Garzikd61780c2005-10-30 15:01:51 -08002053static int sx_ioctl(struct tty_struct * tty, struct file * filp,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002054 unsigned int cmd, unsigned long arg)
2055{
2056 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2057 int retval;
2058 void __user *argp = (void __user *)arg;
2059
2060 func_enter();
2061
2062 if (sx_paranoia_check(port, tty->name, "sx_ioctl")) {
2063 func_exit();
2064 return -ENODEV;
2065 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002066
Linus Torvalds1da177e2005-04-16 15:20:36 -07002067 switch (cmd) {
2068 case TCSBRK: /* SVID version: non-zero arg --> no break */
2069 retval = tty_check_change(tty);
2070 if (retval) {
2071 func_exit();
2072 return retval;
2073 }
2074 tty_wait_until_sent(tty, 0);
2075 if (!arg)
2076 sx_send_break(port, HZ/4); /* 1/4 second */
2077 return 0;
2078 case TCSBRKP: /* support for POSIX tcsendbreak() */
2079 retval = tty_check_change(tty);
2080 if (retval) {
2081 func_exit();
2082 return retval;
2083 }
2084 tty_wait_until_sent(tty, 0);
2085 sx_send_break(port, arg ? arg*(HZ/10) : HZ/4);
2086 func_exit();
2087 return 0;
2088 case TIOCGSOFTCAR:
2089 if (put_user(C_CLOCAL(tty)?1:0, (unsigned long __user *)argp)) {
2090 func_exit();
2091 return -EFAULT;
2092 }
2093 func_exit();
2094 return 0;
2095 case TIOCSSOFTCAR:
2096 if (get_user(arg, (unsigned long __user *) argp)) {
2097 func_exit();
2098 return -EFAULT;
2099 }
2100 tty->termios->c_cflag =
2101 ((tty->termios->c_cflag & ~CLOCAL) |
2102 (arg ? CLOCAL : 0));
2103 func_exit();
2104 return 0;
2105 case TIOCGSERIAL:
2106 func_exit();
2107 return sx_get_serial_info(port, argp);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002108 case TIOCSSERIAL:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002109 func_exit();
2110 return sx_set_serial_info(port, argp);
2111 default:
2112 func_exit();
2113 return -ENOIOCTLCMD;
2114 }
2115 func_exit();
2116 return 0;
2117}
2118
2119
2120static void sx_throttle(struct tty_struct * tty)
2121{
2122 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2123 struct specialix_board *bp;
2124 unsigned long flags;
2125
2126 func_enter();
2127
2128 if (sx_paranoia_check(port, tty->name, "sx_throttle")) {
2129 func_exit();
2130 return;
2131 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002132
Linus Torvalds1da177e2005-04-16 15:20:36 -07002133 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002134
Linus Torvalds1da177e2005-04-16 15:20:36 -07002135 /* Use DTR instead of RTS ! */
Jeff Garzikd61780c2005-10-30 15:01:51 -08002136 if (SX_CRTSCTS (tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002137 port->MSVR &= ~MSVR_DTR;
2138 else {
2139 /* Auch!!! I think the system shouldn't call this then. */
2140 /* Or maybe we're supposed (allowed?) to do our side of hw
Jeff Garzikd61780c2005-10-30 15:01:51 -08002141 handshake anyway, even when hardware handshake is off.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002142 When you see this in your logs, please report.... */
2143 printk (KERN_ERR "sx%d: Need to throttle, but can't (hardware hs is off)\n",
2144 port_No (port));
2145 }
2146 spin_lock_irqsave(&bp->lock, flags);
2147 sx_out(bp, CD186x_CAR, port_No(port));
2148 spin_unlock_irqrestore(&bp->lock, flags);
2149 if (I_IXOFF(tty)) {
2150 spin_unlock_irqrestore(&bp->lock, flags);
2151 sx_wait_CCR(bp);
2152 spin_lock_irqsave(&bp->lock, flags);
2153 sx_out(bp, CD186x_CCR, CCR_SSCH2);
2154 spin_unlock_irqrestore(&bp->lock, flags);
2155 sx_wait_CCR(bp);
2156 }
2157 spin_lock_irqsave(&bp->lock, flags);
2158 sx_out(bp, CD186x_MSVR, port->MSVR);
2159 spin_unlock_irqrestore(&bp->lock, flags);
2160
2161 func_exit();
2162}
2163
2164
2165static void sx_unthrottle(struct tty_struct * tty)
2166{
2167 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2168 struct specialix_board *bp;
2169 unsigned long flags;
2170
2171 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002172
Linus Torvalds1da177e2005-04-16 15:20:36 -07002173 if (sx_paranoia_check(port, tty->name, "sx_unthrottle")) {
2174 func_exit();
2175 return;
2176 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002177
Linus Torvalds1da177e2005-04-16 15:20:36 -07002178 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002179
Linus Torvalds1da177e2005-04-16 15:20:36 -07002180 spin_lock_irqsave(&port->lock, flags);
2181 /* XXXX Use DTR INSTEAD???? */
2182 if (SX_CRTSCTS(tty)) {
2183 port->MSVR |= MSVR_DTR;
2184 } /* Else clause: see remark in "sx_throttle"... */
2185 spin_lock_irqsave(&bp->lock, flags);
2186 sx_out(bp, CD186x_CAR, port_No(port));
2187 spin_unlock_irqrestore(&bp->lock, flags);
2188 if (I_IXOFF(tty)) {
2189 spin_unlock_irqrestore(&port->lock, flags);
2190 sx_wait_CCR(bp);
2191 spin_lock_irqsave(&bp->lock, flags);
2192 sx_out(bp, CD186x_CCR, CCR_SSCH1);
2193 spin_unlock_irqrestore(&bp->lock, flags);
2194 sx_wait_CCR(bp);
2195 spin_lock_irqsave(&port->lock, flags);
2196 }
2197 spin_lock_irqsave(&bp->lock, flags);
2198 sx_out(bp, CD186x_MSVR, port->MSVR);
2199 spin_unlock_irqrestore(&bp->lock, flags);
2200 spin_unlock_irqrestore(&port->lock, flags);
2201
2202 func_exit();
2203}
2204
2205
2206static void sx_stop(struct tty_struct * tty)
2207{
2208 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2209 struct specialix_board *bp;
2210 unsigned long flags;
2211
2212 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002213
Linus Torvalds1da177e2005-04-16 15:20:36 -07002214 if (sx_paranoia_check(port, tty->name, "sx_stop")) {
2215 func_exit();
2216 return;
2217 }
2218
2219 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002220
Linus Torvalds1da177e2005-04-16 15:20:36 -07002221 spin_lock_irqsave(&port->lock, flags);
2222 port->IER &= ~IER_TXRDY;
2223 spin_lock_irqsave(&bp->lock, flags);
2224 sx_out(bp, CD186x_CAR, port_No(port));
2225 sx_out(bp, CD186x_IER, port->IER);
2226 spin_unlock_irqrestore(&bp->lock, flags);
2227 spin_unlock_irqrestore(&port->lock, flags);
2228
2229 func_exit();
2230}
2231
2232
2233static void sx_start(struct tty_struct * tty)
2234{
2235 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2236 struct specialix_board *bp;
2237 unsigned long flags;
2238
2239 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002240
Linus Torvalds1da177e2005-04-16 15:20:36 -07002241 if (sx_paranoia_check(port, tty->name, "sx_start")) {
2242 func_exit();
2243 return;
2244 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002245
Linus Torvalds1da177e2005-04-16 15:20:36 -07002246 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002247
Linus Torvalds1da177e2005-04-16 15:20:36 -07002248 spin_lock_irqsave(&port->lock, flags);
2249 if (port->xmit_cnt && port->xmit_buf && !(port->IER & IER_TXRDY)) {
2250 port->IER |= IER_TXRDY;
2251 spin_lock_irqsave(&bp->lock, flags);
2252 sx_out(bp, CD186x_CAR, port_No(port));
2253 sx_out(bp, CD186x_IER, port->IER);
2254 spin_unlock_irqrestore(&bp->lock, flags);
2255 }
2256 spin_unlock_irqrestore(&port->lock, flags);
2257
2258 func_exit();
2259}
2260
2261
2262/*
2263 * This routine is called from the work-queue when the interrupt
2264 * routine has signalled that a hangup has occurred. The path of
2265 * hangup processing is:
2266 *
2267 * serial interrupt routine -> (workqueue) ->
2268 * do_sx_hangup() -> tty->hangup() -> sx_hangup()
Jeff Garzikd61780c2005-10-30 15:01:51 -08002269 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07002270 */
2271static void do_sx_hangup(void *private_)
2272{
2273 struct specialix_port *port = (struct specialix_port *) private_;
2274 struct tty_struct *tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002275
Linus Torvalds1da177e2005-04-16 15:20:36 -07002276 func_enter();
2277
2278 tty = port->tty;
2279 if (tty)
2280 tty_hangup(tty); /* FIXME: module removal race here */
2281
2282 func_exit();
2283}
2284
2285
2286static void sx_hangup(struct tty_struct * tty)
2287{
2288 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2289 struct specialix_board *bp;
2290 unsigned long flags;
2291
2292 func_enter();
2293
2294 if (sx_paranoia_check(port, tty->name, "sx_hangup")) {
2295 func_exit();
2296 return;
2297 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002298
Linus Torvalds1da177e2005-04-16 15:20:36 -07002299 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002300
Linus Torvalds1da177e2005-04-16 15:20:36 -07002301 sx_shutdown_port(bp, port);
2302 spin_lock_irqsave(&port->lock, flags);
2303 port->event = 0;
2304 bp->count -= port->count;
2305 if (bp->count < 0) {
2306 printk(KERN_ERR "sx%d: sx_hangup: bad board count: %d port: %d\n",
2307 board_No(bp), bp->count, tty->index);
2308 bp->count = 0;
2309 }
2310 port->count = 0;
2311 port->flags &= ~ASYNC_NORMAL_ACTIVE;
2312 port->tty = NULL;
2313 spin_unlock_irqrestore(&port->lock, flags);
2314 wake_up_interruptible(&port->open_wait);
2315
2316 func_exit();
2317}
2318
2319
2320static void sx_set_termios(struct tty_struct * tty, struct termios * old_termios)
2321{
2322 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2323 unsigned long flags;
2324 struct specialix_board * bp;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002325
Linus Torvalds1da177e2005-04-16 15:20:36 -07002326 if (sx_paranoia_check(port, tty->name, "sx_set_termios"))
2327 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002328
Linus Torvalds1da177e2005-04-16 15:20:36 -07002329 if (tty->termios->c_cflag == old_termios->c_cflag &&
2330 tty->termios->c_iflag == old_termios->c_iflag)
2331 return;
2332
2333 bp = port_Board(port);
2334 spin_lock_irqsave(&port->lock, flags);
2335 sx_change_speed(port_Board(port), port);
2336 spin_unlock_irqrestore(&port->lock, flags);
2337
2338 if ((old_termios->c_cflag & CRTSCTS) &&
2339 !(tty->termios->c_cflag & CRTSCTS)) {
2340 tty->hw_stopped = 0;
2341 sx_start(tty);
2342 }
2343}
2344
2345
2346static void do_softint(void *private_)
2347{
2348 struct specialix_port *port = (struct specialix_port *) private_;
2349 struct tty_struct *tty;
2350
2351 func_enter();
2352
2353 if(!(tty = port->tty)) {
2354 func_exit();
2355 return;
2356 }
2357
2358 if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) {
2359 tty_wakeup(tty);
2360 //wake_up_interruptible(&tty->write_wait);
2361 }
2362
2363 func_exit();
2364}
2365
2366static struct tty_operations sx_ops = {
2367 .open = sx_open,
2368 .close = sx_close,
2369 .write = sx_write,
2370 .put_char = sx_put_char,
2371 .flush_chars = sx_flush_chars,
2372 .write_room = sx_write_room,
2373 .chars_in_buffer = sx_chars_in_buffer,
2374 .flush_buffer = sx_flush_buffer,
2375 .ioctl = sx_ioctl,
2376 .throttle = sx_throttle,
2377 .unthrottle = sx_unthrottle,
2378 .set_termios = sx_set_termios,
2379 .stop = sx_stop,
2380 .start = sx_start,
2381 .hangup = sx_hangup,
2382 .tiocmget = sx_tiocmget,
2383 .tiocmset = sx_tiocmset,
2384};
2385
2386static int sx_init_drivers(void)
2387{
2388 int error;
2389 int i;
2390
2391 func_enter();
2392
2393 specialix_driver = alloc_tty_driver(SX_NBOARD * SX_NPORT);
2394 if (!specialix_driver) {
2395 printk(KERN_ERR "sx: Couldn't allocate tty_driver.\n");
2396 func_exit();
2397 return 1;
2398 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002399
Linus Torvalds1da177e2005-04-16 15:20:36 -07002400 specialix_driver->owner = THIS_MODULE;
2401 specialix_driver->name = "ttyW";
2402 specialix_driver->major = SPECIALIX_NORMAL_MAJOR;
2403 specialix_driver->type = TTY_DRIVER_TYPE_SERIAL;
2404 specialix_driver->subtype = SERIAL_TYPE_NORMAL;
2405 specialix_driver->init_termios = tty_std_termios;
2406 specialix_driver->init_termios.c_cflag =
2407 B9600 | CS8 | CREAD | HUPCL | CLOCAL;
2408 specialix_driver->flags = TTY_DRIVER_REAL_RAW;
2409 tty_set_operations(specialix_driver, &sx_ops);
2410
2411 if ((error = tty_register_driver(specialix_driver))) {
2412 put_tty_driver(specialix_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002413 printk(KERN_ERR "sx: Couldn't register specialix IO8+ driver, error = %d\n",
2414 error);
2415 func_exit();
2416 return 1;
2417 }
2418 memset(sx_port, 0, sizeof(sx_port));
2419 for (i = 0; i < SX_NPORT * SX_NBOARD; i++) {
2420 sx_port[i].magic = SPECIALIX_MAGIC;
2421 INIT_WORK(&sx_port[i].tqueue, do_softint, &sx_port[i]);
2422 INIT_WORK(&sx_port[i].tqueue_hangup, do_sx_hangup, &sx_port[i]);
2423 sx_port[i].close_delay = 50 * HZ/100;
2424 sx_port[i].closing_wait = 3000 * HZ/100;
2425 init_waitqueue_head(&sx_port[i].open_wait);
2426 init_waitqueue_head(&sx_port[i].close_wait);
2427 spin_lock_init(&sx_port[i].lock);
2428 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002429
Linus Torvalds1da177e2005-04-16 15:20:36 -07002430 func_exit();
2431 return 0;
2432}
2433
2434static void sx_release_drivers(void)
2435{
2436 func_enter();
2437
Linus Torvalds1da177e2005-04-16 15:20:36 -07002438 tty_unregister_driver(specialix_driver);
2439 put_tty_driver(specialix_driver);
2440 func_exit();
2441}
2442
Jeff Garzikd61780c2005-10-30 15:01:51 -08002443/*
2444 * This routine must be called by kernel at boot time
Linus Torvalds1da177e2005-04-16 15:20:36 -07002445 */
2446static int __init specialix_init(void)
2447{
2448 int i;
2449 int found = 0;
2450
2451 func_enter();
2452
2453 printk(KERN_INFO "sx: Specialix IO8+ driver v" VERSION ", (c) R.E.Wolff 1997/1998.\n");
2454 printk(KERN_INFO "sx: derived from work (c) D.Gorodchanin 1994-1996.\n");
2455#ifdef CONFIG_SPECIALIX_RTSCTS
2456 printk (KERN_INFO "sx: DTR/RTS pin is always RTS.\n");
2457#else
2458 printk (KERN_INFO "sx: DTR/RTS pin is RTS when CRTSCTS is on.\n");
2459#endif
Jeff Garzikd61780c2005-10-30 15:01:51 -08002460
Linus Torvalds1da177e2005-04-16 15:20:36 -07002461 for (i = 0; i < SX_NBOARD; i++)
Ingo Molnar34af9462006-06-27 02:53:55 -07002462 spin_lock_init(&sx_board[i].lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002463
2464 if (sx_init_drivers()) {
2465 func_exit();
2466 return -EIO;
2467 }
2468
Jeff Garzikd61780c2005-10-30 15:01:51 -08002469 for (i = 0; i < SX_NBOARD; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002470 if (sx_board[i].base && !sx_probe(&sx_board[i]))
2471 found++;
2472
2473#ifdef CONFIG_PCI
2474 {
2475 struct pci_dev *pdev = NULL;
2476
2477 i=0;
2478 while (i < SX_NBOARD) {
2479 if (sx_board[i].flags & SX_BOARD_PRESENT) {
2480 i++;
2481 continue;
2482 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002483 pdev = pci_find_device (PCI_VENDOR_ID_SPECIALIX,
2484 PCI_DEVICE_ID_SPECIALIX_IO8,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002485 pdev);
2486 if (!pdev) break;
2487
2488 if (pci_enable_device(pdev))
2489 continue;
2490
2491 sx_board[i].irq = pdev->irq;
2492
2493 sx_board[i].base = pci_resource_start (pdev, 2);
2494
2495 sx_board[i].flags |= SX_BOARD_IS_PCI;
2496 if (!sx_probe(&sx_board[i]))
2497 found ++;
2498 }
2499 }
2500#endif
2501
2502 if (!found) {
2503 sx_release_drivers();
2504 printk(KERN_INFO "sx: No specialix IO8+ boards detected.\n");
2505 func_exit();
2506 return -EIO;
2507 }
2508
2509 func_exit();
2510 return 0;
2511}
2512
2513static int iobase[SX_NBOARD] = {0,};
2514
2515static int irq [SX_NBOARD] = {0,};
2516
2517module_param_array(iobase, int, NULL, 0);
2518module_param_array(irq, int, NULL, 0);
2519module_param(sx_debug, int, 0);
2520module_param(sx_rxfifo, int, 0);
2521#ifdef SPECIALIX_TIMER
2522module_param(sx_poll, int, 0);
2523#endif
2524
2525/*
2526 * You can setup up to 4 boards.
2527 * by specifying "iobase=0xXXX,0xXXX ..." as insmod parameter.
Jeff Garzikd61780c2005-10-30 15:01:51 -08002528 * You should specify the IRQs too in that case "irq=....,...".
2529 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07002530 * More than 4 boards in one computer is not possible, as the card can
Jeff Garzikd61780c2005-10-30 15:01:51 -08002531 * only use 4 different interrupts.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002532 *
2533 */
2534static int __init specialix_init_module(void)
2535{
2536 int i;
2537
2538 func_enter();
2539
Linus Torvalds1da177e2005-04-16 15:20:36 -07002540 if (iobase[0] || iobase[1] || iobase[2] || iobase[3]) {
2541 for(i = 0; i < SX_NBOARD; i++) {
2542 sx_board[i].base = iobase[i];
2543 sx_board[i].irq = irq[i];
2544 sx_board[i].count= 0;
2545 }
2546 }
2547
2548 func_exit();
2549
2550 return specialix_init();
2551}
Jeff Garzikd61780c2005-10-30 15:01:51 -08002552
Linus Torvalds1da177e2005-04-16 15:20:36 -07002553static void __exit specialix_exit_module(void)
2554{
2555 int i;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002556
Linus Torvalds1da177e2005-04-16 15:20:36 -07002557 func_enter();
2558
2559 sx_release_drivers();
2560 for (i = 0; i < SX_NBOARD; i++)
Jeff Garzikd61780c2005-10-30 15:01:51 -08002561 if (sx_board[i].flags & SX_BOARD_PRESENT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002562 sx_release_io_range(&sx_board[i]);
2563#ifdef SPECIALIX_TIMER
2564 del_timer (&missed_irq_timer);
2565#endif
2566
2567 func_exit();
2568}
2569
Chuck Short76910302006-07-10 04:43:59 -07002570static struct pci_device_id specialx_pci_tbl[] __devinitdata = {
2571 { PCI_DEVICE(PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_SPECIALIX_IO8) },
2572 { }
2573};
2574MODULE_DEVICE_TABLE(pci, specialx_pci_tbl);
2575
Linus Torvalds1da177e2005-04-16 15:20:36 -07002576module_init(specialix_init_module);
2577module_exit(specialix_exit_module);
2578
2579MODULE_LICENSE("GPL");