blob: 0a574bdbce3695df8ab4e0553753e9e5f2f7a1f7 [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
78#include <linux/config.h>
79#include <linux/module.h>
80
81#include <asm/io.h>
82#include <linux/kernel.h>
83#include <linux/sched.h>
84#include <linux/ioport.h>
85#include <linux/interrupt.h>
86#include <linux/errno.h>
87#include <linux/tty.h>
Alan Cox33f0f882006-01-09 20:54:13 -080088#include <linux/tty_flip.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070089#include <linux/mm.h>
90#include <linux/serial.h>
91#include <linux/fcntl.h>
92#include <linux/major.h>
93#include <linux/delay.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070094#include <linux/pci.h>
95#include <linux/init.h>
96#include <asm/uaccess.h>
97
98#include "specialix_io8.h"
99#include "cd1865.h"
100
101
102/*
103 This driver can spew a whole lot of debugging output at you. If you
104 need maximum performance, you should disable the DEBUG define. To
105 aid in debugging in the field, I'm leaving the compile-time debug
106 features enabled, and disable them "runtime". That allows me to
107 instruct people with problems to enable debugging without requiring
108 them to recompile...
109*/
110#define DEBUG
111
112static int sx_debug;
113static int sx_rxfifo = SPECIALIX_RXFIFO;
114
115#ifdef DEBUG
116#define dprintk(f, str...) if (sx_debug & f) printk (str)
117#else
118#define dprintk(f, str...) /* nothing */
119#endif
120
121#define SX_DEBUG_FLOW 0x0001
122#define SX_DEBUG_DATA 0x0002
123#define SX_DEBUG_PROBE 0x0004
124#define SX_DEBUG_CHAN 0x0008
125#define SX_DEBUG_INIT 0x0010
126#define SX_DEBUG_RX 0x0020
127#define SX_DEBUG_TX 0x0040
128#define SX_DEBUG_IRQ 0x0080
129#define SX_DEBUG_OPEN 0x0100
130#define SX_DEBUG_TERMIOS 0x0200
131#define SX_DEBUG_SIGNALS 0x0400
132#define SX_DEBUG_FIFO 0x0800
133
134
135#define func_enter() dprintk (SX_DEBUG_FLOW, "io8: enter %s\n",__FUNCTION__)
136#define func_exit() dprintk (SX_DEBUG_FLOW, "io8: exit %s\n", __FUNCTION__)
137
138#define jiffies_from_ms(a) ((((a) * HZ)/1000)+1)
139
140
141/* Configurable options: */
142
143/* Am I paranoid or not ? ;-) */
144#define SPECIALIX_PARANOIA_CHECK
145
146/* Do I trust the IRQ from the card? (enabeling it doesn't seem to help)
147 When the IRQ routine leaves the chip in a state that is keeps on
148 requiring attention, the timer doesn't help either. */
149#undef SPECIALIX_TIMER
150
151#ifdef SPECIALIX_TIMER
152static int sx_poll = HZ;
153#endif
154
155
156
Jeff Garzikd61780c2005-10-30 15:01:51 -0800157/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158 * The following defines are mostly for testing purposes. But if you need
159 * some nice reporting in your syslog, you can define them also.
160 */
161#undef SX_REPORT_FIFO
162#undef SX_REPORT_OVERRUN
163
164
165
166#ifdef CONFIG_SPECIALIX_RTSCTS
167#define SX_CRTSCTS(bla) 1
168#else
169#define SX_CRTSCTS(tty) C_CRTSCTS(tty)
170#endif
171
172
173/* Used to be outb (0xff, 0x80); */
174#define short_pause() udelay (1)
175
176
177#define SPECIALIX_LEGAL_FLAGS \
178 (ASYNC_HUP_NOTIFY | ASYNC_SAK | ASYNC_SPLIT_TERMIOS | \
179 ASYNC_SPD_HI | ASYNC_SPEED_VHI | ASYNC_SESSION_LOCKOUT | \
180 ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP)
181
182#undef RS_EVENT_WRITE_WAKEUP
183#define RS_EVENT_WRITE_WAKEUP 0
184
185static struct tty_driver *specialix_driver;
186static unsigned char * tmp_buf;
187static DECLARE_MUTEX(tmp_buf_sem);
188
189static unsigned long baud_table[] = {
190 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
Jeff Garzikd61780c2005-10-30 15:01:51 -0800191 9600, 19200, 38400, 57600, 115200, 0,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192};
193
194static struct specialix_board sx_board[SX_NBOARD] = {
195 { 0, SX_IOBASE1, 9, },
196 { 0, SX_IOBASE2, 11, },
197 { 0, SX_IOBASE3, 12, },
198 { 0, SX_IOBASE4, 15, },
199};
200
201static struct specialix_port sx_port[SX_NBOARD * SX_NPORT];
202
203
204#ifdef SPECIALIX_TIMER
205static struct timer_list missed_irq_timer;
206static irqreturn_t sx_interrupt(int irq, void * dev_id, struct pt_regs * regs);
207#endif
208
209
210
211static inline int sx_paranoia_check(struct specialix_port const * port,
212 char *name, const char *routine)
213{
214#ifdef SPECIALIX_PARANOIA_CHECK
215 static const char *badmagic =
216 KERN_ERR "sx: Warning: bad specialix port magic number for device %s in %s\n";
217 static const char *badinfo =
218 KERN_ERR "sx: Warning: null specialix port for device %s in %s\n";
Jeff Garzikd61780c2005-10-30 15:01:51 -0800219
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220 if (!port) {
221 printk(badinfo, name, routine);
222 return 1;
223 }
224 if (port->magic != SPECIALIX_MAGIC) {
225 printk(badmagic, name, routine);
226 return 1;
227 }
228#endif
229 return 0;
230}
231
232
233/*
Jeff Garzikd61780c2005-10-30 15:01:51 -0800234 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235 * Service functions for specialix IO8+ driver.
Jeff Garzikd61780c2005-10-30 15:01:51 -0800236 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237 */
238
239/* Get board number from pointer */
240static inline int board_No (struct specialix_board * bp)
241{
242 return bp - sx_board;
243}
244
245
246/* Get port number from pointer */
247static inline int port_No (struct specialix_port const * port)
248{
Jeff Garzikd61780c2005-10-30 15:01:51 -0800249 return SX_PORT(port - sx_port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250}
251
252
253/* Get pointer to board from pointer to port */
254static inline struct specialix_board * port_Board(struct specialix_port const * port)
255{
256 return &sx_board[SX_BOARD(port - sx_port)];
257}
258
259
260/* Input Byte from CL CD186x register */
261static inline unsigned char sx_in(struct specialix_board * bp, unsigned short reg)
262{
263 bp->reg = reg | 0x80;
264 outb (reg | 0x80, bp->base + SX_ADDR_REG);
265 return inb (bp->base + SX_DATA_REG);
266}
267
268
269/* Output Byte to CL CD186x register */
270static inline void sx_out(struct specialix_board * bp, unsigned short reg,
271 unsigned char val)
272{
273 bp->reg = reg | 0x80;
274 outb (reg | 0x80, bp->base + SX_ADDR_REG);
275 outb (val, bp->base + SX_DATA_REG);
276}
277
278
279/* Input Byte from CL CD186x register */
280static inline unsigned char sx_in_off(struct specialix_board * bp, unsigned short reg)
281{
282 bp->reg = reg;
283 outb (reg, bp->base + SX_ADDR_REG);
284 return inb (bp->base + SX_DATA_REG);
285}
286
287
288/* Output Byte to CL CD186x register */
289static inline void sx_out_off(struct specialix_board * bp, unsigned short reg,
290 unsigned char val)
291{
292 bp->reg = reg;
293 outb (reg, bp->base + SX_ADDR_REG);
294 outb (val, bp->base + SX_DATA_REG);
295}
296
297
298/* Wait for Channel Command Register ready */
299static inline void sx_wait_CCR(struct specialix_board * bp)
300{
301 unsigned long delay, flags;
302 unsigned char ccr;
303
304 for (delay = SX_CCR_TIMEOUT; delay; delay--) {
305 spin_lock_irqsave(&bp->lock, flags);
306 ccr = sx_in(bp, CD186x_CCR);
307 spin_unlock_irqrestore(&bp->lock, flags);
308 if (!ccr)
309 return;
310 udelay (1);
311 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800312
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313 printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp));
314}
315
316
317/* Wait for Channel Command Register ready */
318static inline void sx_wait_CCR_off(struct specialix_board * bp)
319{
320 unsigned long delay;
321 unsigned char crr;
322 unsigned long flags;
323
324 for (delay = SX_CCR_TIMEOUT; delay; delay--) {
325 spin_lock_irqsave(&bp->lock, flags);
326 crr = sx_in_off(bp, CD186x_CCR);
327 spin_unlock_irqrestore(&bp->lock, flags);
328 if (!crr)
329 return;
330 udelay (1);
331 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800332
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp));
334}
335
336
337/*
338 * specialix IO8+ IO range functions.
339 */
340
Jeff Garzikd61780c2005-10-30 15:01:51 -0800341static inline int sx_request_io_range(struct specialix_board * bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342{
Jeff Garzikd61780c2005-10-30 15:01:51 -0800343 return request_region(bp->base,
344 bp->flags & SX_BOARD_IS_PCI ? SX_PCI_IO_SPACE : SX_IO_SPACE,
345 "specialix IO8+") == NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346}
347
348
349static inline void sx_release_io_range(struct specialix_board * bp)
350{
Jeff Garzikd61780c2005-10-30 15:01:51 -0800351 release_region(bp->base,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352 bp->flags&SX_BOARD_IS_PCI?SX_PCI_IO_SPACE:SX_IO_SPACE);
353}
354
Jeff Garzikd61780c2005-10-30 15:01:51 -0800355
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356/* Must be called with enabled interrupts */
Jeff Garzikd61780c2005-10-30 15:01:51 -0800357/* Ugly. Very ugly. Don't use this for anything else than initialization
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358 code */
359static inline void sx_long_delay(unsigned long delay)
360{
361 unsigned long i;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800362
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363 for (i = jiffies + delay; time_after(i, jiffies); ) ;
364}
365
366
367
368/* Set the IRQ using the RTS lines that run to the PAL on the board.... */
369static int sx_set_irq ( struct specialix_board *bp)
370{
371 int virq;
372 int i;
373 unsigned long flags;
374
Jeff Garzikd61780c2005-10-30 15:01:51 -0800375 if (bp->flags & SX_BOARD_IS_PCI)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 return 1;
377 switch (bp->irq) {
378 /* In the same order as in the docs... */
379 case 15: virq = 0;break;
380 case 12: virq = 1;break;
381 case 11: virq = 2;break;
382 case 9: virq = 3;break;
383 default: printk (KERN_ERR "Speclialix: cannot set irq to %d.\n", bp->irq);
384 return 0;
385 }
386 spin_lock_irqsave(&bp->lock, flags);
387 for (i=0;i<2;i++) {
388 sx_out(bp, CD186x_CAR, i);
389 sx_out(bp, CD186x_MSVRTS, ((virq >> i) & 0x1)? MSVR_RTS:0);
390 }
391 spin_unlock_irqrestore(&bp->lock, flags);
392 return 1;
393}
394
395
396/* Reset and setup CD186x chip */
397static int sx_init_CD186x(struct specialix_board * bp)
398{
399 unsigned long flags;
400 int scaler;
401 int rv = 1;
402
403 func_enter();
404 sx_wait_CCR_off(bp); /* Wait for CCR ready */
405 spin_lock_irqsave(&bp->lock, flags);
406 sx_out_off(bp, CD186x_CCR, CCR_HARDRESET); /* Reset CD186x chip */
407 spin_unlock_irqrestore(&bp->lock, flags);
408 sx_long_delay(HZ/20); /* Delay 0.05 sec */
409 spin_lock_irqsave(&bp->lock, flags);
410 sx_out_off(bp, CD186x_GIVR, SX_ID); /* Set ID for this chip */
411 sx_out_off(bp, CD186x_GICR, 0); /* Clear all bits */
412 sx_out_off(bp, CD186x_PILR1, SX_ACK_MINT); /* Prio for modem intr */
413 sx_out_off(bp, CD186x_PILR2, SX_ACK_TINT); /* Prio for transmitter intr */
414 sx_out_off(bp, CD186x_PILR3, SX_ACK_RINT); /* Prio for receiver intr */
415 /* Set RegAckEn */
416 sx_out_off(bp, CD186x_SRCR, sx_in (bp, CD186x_SRCR) | SRCR_REGACKEN);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800417
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 /* Setting up prescaler. We need 4 ticks per 1 ms */
419 scaler = SX_OSCFREQ/SPECIALIX_TPS;
420
421 sx_out_off(bp, CD186x_PPRH, scaler >> 8);
422 sx_out_off(bp, CD186x_PPRL, scaler & 0xff);
423 spin_unlock_irqrestore(&bp->lock, flags);
424
425 if (!sx_set_irq (bp)) {
426 /* Figure out how to pass this along... */
427 printk (KERN_ERR "Cannot set irq to %d.\n", bp->irq);
428 rv = 0;
429 }
430
431 func_exit();
432 return rv;
433}
434
435
436static int read_cross_byte (struct specialix_board *bp, int reg, int bit)
437{
438 int i;
439 int t;
440 unsigned long flags;
441
442 spin_lock_irqsave(&bp->lock, flags);
443 for (i=0, t=0;i<8;i++) {
444 sx_out_off (bp, CD186x_CAR, i);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800445 if (sx_in_off (bp, reg) & bit)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446 t |= 1 << i;
447 }
448 spin_unlock_irqrestore(&bp->lock, flags);
449
450 return t;
451}
452
453
454#ifdef SPECIALIX_TIMER
455void missed_irq (unsigned long data)
456{
457 unsigned char irq;
458 unsigned long flags;
459 struct specialix_board *bp = (struct specialix_board *)data;
460
461 spin_lock_irqsave(&bp->lock, flags);
462 irq = sx_in ((struct specialix_board *)data, CD186x_SRSR) &
463 (SRSR_RREQint |
464 SRSR_TREQint |
465 SRSR_MREQint);
466 spin_unlock_irqrestore(&bp->lock, flags);
467 if (irq) {
468 printk (KERN_INFO "Missed interrupt... Calling int from timer. \n");
Jeff Garzikd61780c2005-10-30 15:01:51 -0800469 sx_interrupt (((struct specialix_board *)data)->irq,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470 (void*)data, NULL);
471 }
472 missed_irq_timer.expires = jiffies + sx_poll;
473 add_timer (&missed_irq_timer);
474}
475#endif
476
477
478
479/* Main probing routine, also sets irq. */
480static int sx_probe(struct specialix_board *bp)
481{
482 unsigned char val1, val2;
483#if 0
484 int irqs = 0;
485 int retries;
486#endif
487 int rev;
488 int chip;
489
490 func_enter();
491
Jeff Garzikd61780c2005-10-30 15:01:51 -0800492 if (sx_request_io_range(bp)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493 func_exit();
494 return 1;
495 }
496
497 /* Are the I/O ports here ? */
498 sx_out_off(bp, CD186x_PPRL, 0x5a);
499 short_pause ();
500 val1 = sx_in_off(bp, CD186x_PPRL);
501
502 sx_out_off(bp, CD186x_PPRL, 0xa5);
503 short_pause ();
504 val2 = sx_in_off(bp, CD186x_PPRL);
505
Jeff Garzikd61780c2005-10-30 15:01:51 -0800506
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507 if ((val1 != 0x5a) || (val2 != 0xa5)) {
508 printk(KERN_INFO "sx%d: specialix IO8+ Board at 0x%03x not found.\n",
509 board_No(bp), bp->base);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800510 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 func_exit();
512 return 1;
513 }
514
Jeff Garzikd61780c2005-10-30 15:01:51 -0800515 /* Check the DSR lines that Specialix uses as board
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516 identification */
517 val1 = read_cross_byte (bp, CD186x_MSVR, MSVR_DSR);
518 val2 = read_cross_byte (bp, CD186x_MSVR, MSVR_RTS);
519 dprintk (SX_DEBUG_INIT, "sx%d: DSR lines are: %02x, rts lines are: %02x\n",
520 board_No(bp), val1, val2);
521
522 /* They managed to switch the bit order between the docs and
523 the IO8+ card. The new PCI card now conforms to old docs.
524 They changed the PCI docs to reflect the situation on the
525 old card. */
526 val2 = (bp->flags & SX_BOARD_IS_PCI)?0x4d : 0xb2;
527 if (val1 != val2) {
528 printk(KERN_INFO "sx%d: specialix IO8+ ID %02x at 0x%03x not found (%02x).\n",
529 board_No(bp), val2, bp->base, val1);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800530 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 func_exit();
532 return 1;
533 }
534
535
536#if 0
537 /* It's time to find IRQ for this board */
538 for (retries = 0; retries < 5 && irqs <= 0; retries++) {
539 irqs = probe_irq_on();
540 sx_init_CD186x(bp); /* Reset CD186x chip */
541 sx_out(bp, CD186x_CAR, 2); /* Select port 2 */
542 sx_wait_CCR(bp);
543 sx_out(bp, CD186x_CCR, CCR_TXEN); /* Enable transmitter */
544 sx_out(bp, CD186x_IER, IER_TXRDY); /* Enable tx empty intr */
Jeff Garzikd61780c2005-10-30 15:01:51 -0800545 sx_long_delay(HZ/20);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 irqs = probe_irq_off(irqs);
547
548 dprintk (SX_DEBUG_INIT, "SRSR = %02x, ", sx_in(bp, CD186x_SRSR));
549 dprintk (SX_DEBUG_INIT, "TRAR = %02x, ", sx_in(bp, CD186x_TRAR));
550 dprintk (SX_DEBUG_INIT, "GIVR = %02x, ", sx_in(bp, CD186x_GIVR));
551 dprintk (SX_DEBUG_INIT, "GICR = %02x, ", sx_in(bp, CD186x_GICR));
552 dprintk (SX_DEBUG_INIT, "\n");
553
554 /* Reset CD186x again */
555 if (!sx_init_CD186x(bp)) {
556 /* Hmmm. This is dead code anyway. */
557 }
558
559 dprintk (SX_DEBUG_INIT "val1 = %02x, val2 = %02x, val3 = %02x.\n",
Jeff Garzikd61780c2005-10-30 15:01:51 -0800560 val1, val2, val3);
561
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800563
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564#if 0
565 if (irqs <= 0) {
566 printk(KERN_ERR "sx%d: Can't find IRQ for specialix IO8+ board at 0x%03x.\n",
567 board_No(bp), bp->base);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800568 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 func_exit();
570 return 1;
571 }
572#endif
573 printk (KERN_INFO "Started with irq=%d, but now have irq=%d.\n", bp->irq, irqs);
574 if (irqs > 0)
575 bp->irq = irqs;
576#endif
577 /* Reset CD186x again */
578 if (!sx_init_CD186x(bp)) {
Jeff Garzikd61780c2005-10-30 15:01:51 -0800579 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580 func_exit();
Jeff Garzikd61780c2005-10-30 15:01:51 -0800581 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582 }
583
584 sx_request_io_range(bp);
585 bp->flags |= SX_BOARD_PRESENT;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800586
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587 /* Chip revcode pkgtype
588 GFRCR SRCR bit 7
589 CD180 rev B 0x81 0
590 CD180 rev C 0x82 0
591 CD1864 rev A 0x82 1
Jeff Garzikd61780c2005-10-30 15:01:51 -0800592 CD1865 rev A 0x83 1 -- Do not use!!! Does not work.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 CD1865 rev B 0x84 1
594 -- Thanks to Gwen Wang, Cirrus Logic.
595 */
596
597 switch (sx_in_off(bp, CD186x_GFRCR)) {
598 case 0x82:chip = 1864;rev='A';break;
599 case 0x83:chip = 1865;rev='A';break;
600 case 0x84:chip = 1865;rev='B';break;
601 case 0x85:chip = 1865;rev='C';break; /* Does not exist at this time */
602 default:chip=-1;rev='x';
603 }
604
605 dprintk (SX_DEBUG_INIT, " GFCR = 0x%02x\n", sx_in_off(bp, CD186x_GFRCR) );
606
607#ifdef SPECIALIX_TIMER
608 init_timer (&missed_irq_timer);
609 missed_irq_timer.function = missed_irq;
610 missed_irq_timer.data = (unsigned long) bp;
611 missed_irq_timer.expires = jiffies + sx_poll;
612 add_timer (&missed_irq_timer);
613#endif
614
615 printk(KERN_INFO"sx%d: specialix IO8+ board detected at 0x%03x, IRQ %d, CD%d Rev. %c.\n",
616 board_No(bp),
617 bp->base, bp->irq,
618 chip, rev);
619
620 func_exit();
621 return 0;
622}
623
Jeff Garzikd61780c2005-10-30 15:01:51 -0800624/*
625 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626 * Interrupt processing routines.
627 * */
628
629static inline void sx_mark_event(struct specialix_port * port, int event)
630{
631 func_enter();
632
633 set_bit(event, &port->event);
634 schedule_work(&port->tqueue);
635
636 func_exit();
637}
638
639
640static inline struct specialix_port * sx_get_port(struct specialix_board * bp,
641 unsigned char const * what)
642{
643 unsigned char channel;
644 struct specialix_port * port = NULL;
645
646 channel = sx_in(bp, CD186x_GICR) >> GICR_CHAN_OFF;
647 dprintk (SX_DEBUG_CHAN, "channel: %d\n", channel);
648 if (channel < CD186x_NCH) {
649 port = &sx_port[board_No(bp) * SX_NPORT + channel];
650 dprintk (SX_DEBUG_CHAN, "port: %d %p flags: 0x%x\n",board_No(bp) * SX_NPORT + channel, port, port->flags & ASYNC_INITIALIZED);
651
652 if (port->flags & ASYNC_INITIALIZED) {
653 dprintk (SX_DEBUG_CHAN, "port: %d %p\n", channel, port);
654 func_exit();
655 return port;
656 }
657 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800658 printk(KERN_INFO "sx%d: %s interrupt from invalid port %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659 board_No(bp), what, channel);
660 return NULL;
661}
662
663
664static inline void sx_receive_exc(struct specialix_board * bp)
665{
666 struct specialix_port *port;
667 struct tty_struct *tty;
668 unsigned char status;
Alan Cox33f0f882006-01-09 20:54:13 -0800669 unsigned char ch, flag;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670
671 func_enter();
672
673 port = sx_get_port(bp, "Receive");
674 if (!port) {
675 dprintk (SX_DEBUG_RX, "Hmm, couldn't find port.\n");
676 func_exit();
677 return;
678 }
679 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800680
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681 status = sx_in(bp, CD186x_RCSR);
682
683 dprintk (SX_DEBUG_RX, "status: 0x%x\n", status);
684 if (status & RCSR_OE) {
685 port->overrun++;
686 dprintk(SX_DEBUG_FIFO, "sx%d: port %d: Overrun. Total %ld overruns.\n",
687 board_No(bp), port_No(port), port->overrun);
688 }
689 status &= port->mark_mask;
690
691 /* This flip buffer check needs to be below the reading of the
692 status register to reset the chip's IRQ.... */
Alan Cox33f0f882006-01-09 20:54:13 -0800693 if (tty_buffer_request_room(tty, 1) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694 dprintk(SX_DEBUG_FIFO, "sx%d: port %d: Working around flip buffer overflow.\n",
695 board_No(bp), port_No(port));
696 func_exit();
697 return;
698 }
699
700 ch = sx_in(bp, CD186x_RDR);
701 if (!status) {
702 func_exit();
703 return;
704 }
705 if (status & RCSR_TOUT) {
Jeff Garzikd61780c2005-10-30 15:01:51 -0800706 printk(KERN_INFO "sx%d: port %d: Receiver timeout. Hardware problems ?\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707 board_No(bp), port_No(port));
708 func_exit();
709 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800710
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711 } else if (status & RCSR_BREAK) {
712 dprintk(SX_DEBUG_RX, "sx%d: port %d: Handling break...\n",
713 board_No(bp), port_No(port));
Alan Cox33f0f882006-01-09 20:54:13 -0800714 flag = TTY_BREAK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715 if (port->flags & ASYNC_SAK)
716 do_SAK(tty);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800717
718 } else if (status & RCSR_PE)
Alan Cox33f0f882006-01-09 20:54:13 -0800719 flag = TTY_PARITY;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800720
721 else if (status & RCSR_FE)
Alan Cox33f0f882006-01-09 20:54:13 -0800722 flag = TTY_FRAME;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800723
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 else if (status & RCSR_OE)
Alan Cox33f0f882006-01-09 20:54:13 -0800725 flag = TTY_OVERRUN;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800726
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727 else
Alan Cox33f0f882006-01-09 20:54:13 -0800728 flag = TTY_NORMAL;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800729
Alan Cox33f0f882006-01-09 20:54:13 -0800730 if(tty_insert_flip_char(tty, ch, flag))
731 tty_flip_buffer_push(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732 func_exit();
733}
734
735
736static inline void sx_receive(struct specialix_board * bp)
737{
738 struct specialix_port *port;
739 struct tty_struct *tty;
740 unsigned char count;
741
742 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -0800743
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744 if (!(port = sx_get_port(bp, "Receive"))) {
745 dprintk (SX_DEBUG_RX, "Hmm, couldn't find port.\n");
746 func_exit();
747 return;
748 }
749 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800750
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751 count = sx_in(bp, CD186x_RDCR);
752 dprintk (SX_DEBUG_RX, "port: %p: count: %d\n", port, count);
753 port->hits[count > 8 ? 9 : count]++;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800754
Alan Cox33f0f882006-01-09 20:54:13 -0800755 tty_buffer_request_room(tty, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756
Alan Cox33f0f882006-01-09 20:54:13 -0800757 while (count--)
758 tty_insert_flip_char(tty, sx_in(bp, CD186x_RDR), TTY_NORMAL);
759 tty_flip_buffer_push(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760 func_exit();
761}
762
763
764static inline void sx_transmit(struct specialix_board * bp)
765{
766 struct specialix_port *port;
767 struct tty_struct *tty;
768 unsigned char count;
769
770 func_enter();
771 if (!(port = sx_get_port(bp, "Transmit"))) {
772 func_exit();
773 return;
774 }
775 dprintk (SX_DEBUG_TX, "port: %p\n", port);
776 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800777
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778 if (port->IER & IER_TXEMPTY) {
779 /* FIFO drained */
780 sx_out(bp, CD186x_CAR, port_No(port));
781 port->IER &= ~IER_TXEMPTY;
782 sx_out(bp, CD186x_IER, port->IER);
783 func_exit();
784 return;
785 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800786
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787 if ((port->xmit_cnt <= 0 && !port->break_length)
788 || tty->stopped || tty->hw_stopped) {
789 sx_out(bp, CD186x_CAR, port_No(port));
790 port->IER &= ~IER_TXRDY;
791 sx_out(bp, CD186x_IER, port->IER);
792 func_exit();
793 return;
794 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800795
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796 if (port->break_length) {
797 if (port->break_length > 0) {
798 if (port->COR2 & COR2_ETC) {
799 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
800 sx_out(bp, CD186x_TDR, CD186x_C_SBRK);
801 port->COR2 &= ~COR2_ETC;
802 }
803 count = min_t(int, port->break_length, 0xff);
804 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
805 sx_out(bp, CD186x_TDR, CD186x_C_DELAY);
806 sx_out(bp, CD186x_TDR, count);
807 if (!(port->break_length -= count))
808 port->break_length--;
809 } else {
810 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
811 sx_out(bp, CD186x_TDR, CD186x_C_EBRK);
812 sx_out(bp, CD186x_COR2, port->COR2);
813 sx_wait_CCR(bp);
814 sx_out(bp, CD186x_CCR, CCR_CORCHG2);
815 port->break_length = 0;
816 }
817
818 func_exit();
819 return;
820 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800821
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822 count = CD186x_NFIFO;
823 do {
824 sx_out(bp, CD186x_TDR, port->xmit_buf[port->xmit_tail++]);
825 port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1);
826 if (--port->xmit_cnt <= 0)
827 break;
828 } while (--count > 0);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800829
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830 if (port->xmit_cnt <= 0) {
831 sx_out(bp, CD186x_CAR, port_No(port));
832 port->IER &= ~IER_TXRDY;
833 sx_out(bp, CD186x_IER, port->IER);
834 }
835 if (port->xmit_cnt <= port->wakeup_chars)
836 sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);
837
838 func_exit();
839}
840
841
842static inline void sx_check_modem(struct specialix_board * bp)
843{
844 struct specialix_port *port;
845 struct tty_struct *tty;
846 unsigned char mcr;
847 int msvr_cd;
848
849 dprintk (SX_DEBUG_SIGNALS, "Modem intr. ");
850 if (!(port = sx_get_port(bp, "Modem")))
851 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800852
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800854
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855 mcr = sx_in(bp, CD186x_MCR);
856 printk ("mcr = %02x.\n", mcr);
857
858 if ((mcr & MCR_CDCHG)) {
859 dprintk (SX_DEBUG_SIGNALS, "CD just changed... ");
860 msvr_cd = sx_in(bp, CD186x_MSVR) & MSVR_CD;
861 if (msvr_cd) {
862 dprintk (SX_DEBUG_SIGNALS, "Waking up guys in open.\n");
863 wake_up_interruptible(&port->open_wait);
864 } else {
865 dprintk (SX_DEBUG_SIGNALS, "Sending HUP.\n");
866 schedule_work(&port->tqueue_hangup);
867 }
868 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800869
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
871 if (mcr & MCR_CTSCHG) {
872 if (sx_in(bp, CD186x_MSVR) & MSVR_CTS) {
873 tty->hw_stopped = 0;
874 port->IER |= IER_TXRDY;
875 if (port->xmit_cnt <= port->wakeup_chars)
876 sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);
877 } else {
878 tty->hw_stopped = 1;
879 port->IER &= ~IER_TXRDY;
880 }
881 sx_out(bp, CD186x_IER, port->IER);
882 }
883 if (mcr & MCR_DSSXHG) {
884 if (sx_in(bp, CD186x_MSVR) & MSVR_DSR) {
885 tty->hw_stopped = 0;
886 port->IER |= IER_TXRDY;
887 if (port->xmit_cnt <= port->wakeup_chars)
888 sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);
889 } else {
890 tty->hw_stopped = 1;
891 port->IER &= ~IER_TXRDY;
892 }
893 sx_out(bp, CD186x_IER, port->IER);
894 }
895#endif /* SPECIALIX_BRAIN_DAMAGED_CTS */
Jeff Garzikd61780c2005-10-30 15:01:51 -0800896
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897 /* Clear change bits */
898 sx_out(bp, CD186x_MCR, 0);
899}
900
901
902/* The main interrupt processing routine */
903static irqreturn_t sx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
904{
905 unsigned char status;
906 unsigned char ack;
907 struct specialix_board *bp;
908 unsigned long loop = 0;
909 int saved_reg;
910 unsigned long flags;
911
912 func_enter();
913
914 bp = dev_id;
915 spin_lock_irqsave(&bp->lock, flags);
916
917 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);
918 if (!bp || !(bp->flags & SX_BOARD_ACTIVE)) {
919 dprintk (SX_DEBUG_IRQ, "sx: False interrupt. irq %d.\n", irq);
920 spin_unlock_irqrestore(&bp->lock, flags);
921 func_exit();
922 return IRQ_NONE;
923 }
924
925 saved_reg = bp->reg;
926
927 while ((++loop < 16) && (status = (sx_in(bp, CD186x_SRSR) &
928 (SRSR_RREQint |
929 SRSR_TREQint |
Jeff Garzikd61780c2005-10-30 15:01:51 -0800930 SRSR_MREQint)))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931 if (status & SRSR_RREQint) {
932 ack = sx_in(bp, CD186x_RRAR);
933
934 if (ack == (SX_ID | GIVR_IT_RCV))
935 sx_receive(bp);
936 else if (ack == (SX_ID | GIVR_IT_REXC))
937 sx_receive_exc(bp);
938 else
939 printk(KERN_ERR "sx%d: status: 0x%x Bad receive ack 0x%02x.\n",
940 board_No(bp), status, ack);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800941
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942 } else if (status & SRSR_TREQint) {
943 ack = sx_in(bp, CD186x_TRAR);
944
945 if (ack == (SX_ID | GIVR_IT_TX))
946 sx_transmit(bp);
947 else
948 printk(KERN_ERR "sx%d: status: 0x%x Bad transmit ack 0x%02x. port: %d\n",
949 board_No(bp), status, ack, port_No (sx_get_port (bp, "Int")));
950 } else if (status & SRSR_MREQint) {
951 ack = sx_in(bp, CD186x_MRAR);
952
Jeff Garzikd61780c2005-10-30 15:01:51 -0800953 if (ack == (SX_ID | GIVR_IT_MODEM))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954 sx_check_modem(bp);
955 else
956 printk(KERN_ERR "sx%d: status: 0x%x Bad modem ack 0x%02x.\n",
957 board_No(bp), status, ack);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800958
959 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960
961 sx_out(bp, CD186x_EOIR, 0); /* Mark end of interrupt */
962 }
963 bp->reg = saved_reg;
964 outb (bp->reg, bp->base + SX_ADDR_REG);
965 spin_unlock_irqrestore(&bp->lock, flags);
966 func_exit();
967 return IRQ_HANDLED;
968}
969
970
971/*
972 * Routines for open & close processing.
973 */
974
975static void turn_ints_off (struct specialix_board *bp)
976{
977 unsigned long flags;
978
979 func_enter();
980 if (bp->flags & SX_BOARD_IS_PCI) {
981 /* This was intended for enabeling the interrupt on the
982 * PCI card. However it seems that it's already enabled
983 * and as PCI interrupts can be shared, there is no real
984 * reason to have to turn it off. */
985 }
986
987 spin_lock_irqsave(&bp->lock, flags);
988 (void) sx_in_off (bp, 0); /* Turn off interrupts. */
989 spin_unlock_irqrestore(&bp->lock, flags);
990
991 func_exit();
992}
993
994static void turn_ints_on (struct specialix_board *bp)
995{
996 unsigned long flags;
997
998 func_enter();
999
1000 if (bp->flags & SX_BOARD_IS_PCI) {
1001 /* play with the PCI chip. See comment above. */
1002 }
1003 spin_lock_irqsave(&bp->lock, flags);
1004 (void) sx_in (bp, 0); /* Turn ON interrupts. */
1005 spin_unlock_irqrestore(&bp->lock, flags);
1006
1007 func_exit();
1008}
1009
1010
1011/* Called with disabled interrupts */
1012static inline int sx_setup_board(struct specialix_board * bp)
1013{
1014 int error;
1015
Jeff Garzikd61780c2005-10-30 15:01:51 -08001016 if (bp->flags & SX_BOARD_ACTIVE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017 return 0;
1018
1019 if (bp->flags & SX_BOARD_IS_PCI)
1020 error = request_irq(bp->irq, sx_interrupt, SA_INTERRUPT | SA_SHIRQ, "specialix IO8+", bp);
1021 else
1022 error = request_irq(bp->irq, sx_interrupt, SA_INTERRUPT, "specialix IO8+", bp);
1023
Jeff Garzikd61780c2005-10-30 15:01:51 -08001024 if (error)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025 return error;
1026
1027 turn_ints_on (bp);
1028 bp->flags |= SX_BOARD_ACTIVE;
1029
1030 return 0;
1031}
1032
1033
1034/* Called with disabled interrupts */
1035static inline void sx_shutdown_board(struct specialix_board *bp)
1036{
1037 func_enter();
1038
1039 if (!(bp->flags & SX_BOARD_ACTIVE)) {
1040 func_exit();
1041 return;
1042 }
1043
1044 bp->flags &= ~SX_BOARD_ACTIVE;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001045
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046 dprintk (SX_DEBUG_IRQ, "Freeing IRQ%d for board %d.\n",
1047 bp->irq, board_No (bp));
1048 free_irq(bp->irq, bp);
1049
1050 turn_ints_off (bp);
1051
1052
1053 func_exit();
1054}
1055
1056
1057/*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001058 * Setting up port characteristics.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059 * Must be called with disabled interrupts
1060 */
1061static void sx_change_speed(struct specialix_board *bp, struct specialix_port *port)
1062{
1063 struct tty_struct *tty;
1064 unsigned long baud;
1065 long tmp;
1066 unsigned char cor1 = 0, cor3 = 0;
1067 unsigned char mcor1 = 0, mcor2 = 0;
1068 static unsigned long again;
1069 unsigned long flags;
1070
1071 func_enter();
1072
1073 if (!(tty = port->tty) || !tty->termios) {
1074 func_exit();
1075 return;
1076 }
1077
1078 port->IER = 0;
1079 port->COR2 = 0;
1080 /* Select port on the board */
1081 spin_lock_irqsave(&bp->lock, flags);
1082 sx_out(bp, CD186x_CAR, port_No(port));
1083
1084 /* The Specialix board doens't implement the RTS lines.
1085 They are used to set the IRQ level. Don't touch them. */
1086 if (SX_CRTSCTS(tty))
1087 port->MSVR = MSVR_DTR | (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
1088 else
1089 port->MSVR = (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
1090 spin_unlock_irqrestore(&bp->lock, flags);
1091 dprintk (SX_DEBUG_TERMIOS, "sx: got MSVR=%02x.\n", port->MSVR);
1092 baud = C_BAUD(tty);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001093
Linus Torvalds1da177e2005-04-16 15:20:36 -07001094 if (baud & CBAUDEX) {
1095 baud &= ~CBAUDEX;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001096 if (baud < 1 || baud > 2)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001097 port->tty->termios->c_cflag &= ~CBAUDEX;
1098 else
1099 baud += 15;
1100 }
1101 if (baud == 15) {
1102 if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
1103 baud ++;
1104 if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
1105 baud += 2;
1106 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001107
1108
Linus Torvalds1da177e2005-04-16 15:20:36 -07001109 if (!baud_table[baud]) {
1110 /* Drop DTR & exit */
1111 dprintk (SX_DEBUG_TERMIOS, "Dropping DTR... Hmm....\n");
1112 if (!SX_CRTSCTS (tty)) {
1113 port -> MSVR &= ~ MSVR_DTR;
1114 spin_lock_irqsave(&bp->lock, flags);
1115 sx_out(bp, CD186x_MSVR, port->MSVR );
1116 spin_unlock_irqrestore(&bp->lock, flags);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001117 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118 else
1119 dprintk (SX_DEBUG_TERMIOS, "Can't drop DTR: no DTR.\n");
1120 return;
1121 } else {
1122 /* Set DTR on */
1123 if (!SX_CRTSCTS (tty)) {
1124 port ->MSVR |= MSVR_DTR;
1125 }
1126 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001127
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128 /*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001129 * Now we must calculate some speed depended things
Linus Torvalds1da177e2005-04-16 15:20:36 -07001130 */
1131
1132 /* Set baud rate for port */
1133 tmp = port->custom_divisor ;
1134 if ( tmp )
1135 printk (KERN_INFO "sx%d: Using custom baud rate divisor %ld. \n"
1136 "This is an untested option, please be carefull.\n",
1137 port_No (port), tmp);
1138 else
1139 tmp = (((SX_OSCFREQ + baud_table[baud]/2) / baud_table[baud] +
1140 CD186x_TPC/2) / CD186x_TPC);
1141
Jeff Garzikd61780c2005-10-30 15:01:51 -08001142 if ((tmp < 0x10) && time_before(again, jiffies)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143 again = jiffies + HZ * 60;
1144 /* Page 48 of version 2.0 of the CL-CD1865 databook */
1145 if (tmp >= 12) {
1146 printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
1147 "Performance degradation is possible.\n"
1148 "Read specialix.txt for more info.\n",
1149 port_No (port), tmp);
1150 } else {
1151 printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
1152 "Warning: overstressing Cirrus chip. "
1153 "This might not work.\n"
Jeff Garzikd61780c2005-10-30 15:01:51 -08001154 "Read specialix.txt for more info.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155 port_No (port), tmp);
1156 }
1157 }
1158 spin_lock_irqsave(&bp->lock, flags);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001159 sx_out(bp, CD186x_RBPRH, (tmp >> 8) & 0xff);
1160 sx_out(bp, CD186x_TBPRH, (tmp >> 8) & 0xff);
1161 sx_out(bp, CD186x_RBPRL, tmp & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 sx_out(bp, CD186x_TBPRL, tmp & 0xff);
1163 spin_unlock_irqrestore(&bp->lock, flags);
1164 if (port->custom_divisor) {
1165 baud = (SX_OSCFREQ + port->custom_divisor/2) / port->custom_divisor;
1166 baud = ( baud + 5 ) / 10;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001167 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168 baud = (baud_table[baud] + 5) / 10; /* Estimated CPS */
1169
1170 /* Two timer ticks seems enough to wakeup something like SLIP driver */
Jeff Garzikd61780c2005-10-30 15:01:51 -08001171 tmp = ((baud + HZ/2) / HZ) * 2 - CD186x_NFIFO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172 port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ?
1173 SERIAL_XMIT_SIZE - 1 : tmp);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001174
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175 /* Receiver timeout will be transmission time for 1.5 chars */
1176 tmp = (SPECIALIX_TPS + SPECIALIX_TPS/2 + baud/2) / baud;
1177 tmp = (tmp > 0xff) ? 0xff : tmp;
1178 spin_lock_irqsave(&bp->lock, flags);
1179 sx_out(bp, CD186x_RTPR, tmp);
1180 spin_unlock_irqrestore(&bp->lock, flags);
1181 switch (C_CSIZE(tty)) {
1182 case CS5:
1183 cor1 |= COR1_5BITS;
1184 break;
1185 case CS6:
1186 cor1 |= COR1_6BITS;
1187 break;
1188 case CS7:
1189 cor1 |= COR1_7BITS;
1190 break;
1191 case CS8:
1192 cor1 |= COR1_8BITS;
1193 break;
1194 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001195
1196 if (C_CSTOPB(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001197 cor1 |= COR1_2SB;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001198
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199 cor1 |= COR1_IGNORE;
1200 if (C_PARENB(tty)) {
1201 cor1 |= COR1_NORMPAR;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001202 if (C_PARODD(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001203 cor1 |= COR1_ODDP;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001204 if (I_INPCK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001205 cor1 &= ~COR1_IGNORE;
1206 }
1207 /* Set marking of some errors */
1208 port->mark_mask = RCSR_OE | RCSR_TOUT;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001209 if (I_INPCK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210 port->mark_mask |= RCSR_FE | RCSR_PE;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001211 if (I_BRKINT(tty) || I_PARMRK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212 port->mark_mask |= RCSR_BREAK;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001213 if (I_IGNPAR(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214 port->mark_mask &= ~(RCSR_FE | RCSR_PE);
1215 if (I_IGNBRK(tty)) {
1216 port->mark_mask &= ~RCSR_BREAK;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001217 if (I_IGNPAR(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218 /* Real raw mode. Ignore all */
1219 port->mark_mask &= ~RCSR_OE;
1220 }
1221 /* Enable Hardware Flow Control */
1222 if (C_CRTSCTS(tty)) {
1223#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
1224 port->IER |= IER_DSR | IER_CTS;
1225 mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD;
1226 mcor2 |= MCOR2_DSROD | MCOR2_CTSOD;
1227 spin_lock_irqsave(&bp->lock, flags);
1228 tty->hw_stopped = !(sx_in(bp, CD186x_MSVR) & (MSVR_CTS|MSVR_DSR));
1229 spin_unlock_irqrestore(&bp->lock, flags);
1230#else
Jeff Garzikd61780c2005-10-30 15:01:51 -08001231 port->COR2 |= COR2_CTSAE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001232#endif
1233 }
1234 /* Enable Software Flow Control. FIXME: I'm not sure about this */
1235 /* Some people reported that it works, but I still doubt it */
1236 if (I_IXON(tty)) {
1237 port->COR2 |= COR2_TXIBE;
1238 cor3 |= (COR3_FCT | COR3_SCDE);
1239 if (I_IXANY(tty))
1240 port->COR2 |= COR2_IXM;
1241 spin_lock_irqsave(&bp->lock, flags);
1242 sx_out(bp, CD186x_SCHR1, START_CHAR(tty));
1243 sx_out(bp, CD186x_SCHR2, STOP_CHAR(tty));
1244 sx_out(bp, CD186x_SCHR3, START_CHAR(tty));
1245 sx_out(bp, CD186x_SCHR4, STOP_CHAR(tty));
1246 spin_unlock_irqrestore(&bp->lock, flags);
1247 }
1248 if (!C_CLOCAL(tty)) {
1249 /* Enable CD check */
1250 port->IER |= IER_CD;
1251 mcor1 |= MCOR1_CDZD;
1252 mcor2 |= MCOR2_CDOD;
1253 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001254
1255 if (C_CREAD(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001256 /* Enable receiver */
1257 port->IER |= IER_RXD;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001258
Linus Torvalds1da177e2005-04-16 15:20:36 -07001259 /* Set input FIFO size (1-8 bytes) */
1260 cor3 |= sx_rxfifo;
1261 /* Setting up CD186x channel registers */
1262 spin_lock_irqsave(&bp->lock, flags);
1263 sx_out(bp, CD186x_COR1, cor1);
1264 sx_out(bp, CD186x_COR2, port->COR2);
1265 sx_out(bp, CD186x_COR3, cor3);
1266 spin_unlock_irqrestore(&bp->lock, flags);
1267 /* Make CD186x know about registers change */
1268 sx_wait_CCR(bp);
1269 spin_lock_irqsave(&bp->lock, flags);
1270 sx_out(bp, CD186x_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3);
1271 /* Setting up modem option registers */
1272 dprintk (SX_DEBUG_TERMIOS, "Mcor1 = %02x, mcor2 = %02x.\n", mcor1, mcor2);
1273 sx_out(bp, CD186x_MCOR1, mcor1);
1274 sx_out(bp, CD186x_MCOR2, mcor2);
1275 spin_unlock_irqrestore(&bp->lock, flags);
1276 /* Enable CD186x transmitter & receiver */
1277 sx_wait_CCR(bp);
1278 spin_lock_irqsave(&bp->lock, flags);
1279 sx_out(bp, CD186x_CCR, CCR_TXEN | CCR_RXEN);
1280 /* Enable interrupts */
1281 sx_out(bp, CD186x_IER, port->IER);
1282 /* And finally set the modem lines... */
1283 sx_out(bp, CD186x_MSVR, port->MSVR);
1284 spin_unlock_irqrestore(&bp->lock, flags);
1285
1286 func_exit();
1287}
1288
1289
1290/* Must be called with interrupts enabled */
1291static int sx_setup_port(struct specialix_board *bp, struct specialix_port *port)
1292{
1293 unsigned long flags;
1294
1295 func_enter();
1296
1297 if (port->flags & ASYNC_INITIALIZED) {
1298 func_exit();
1299 return 0;
1300 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001301
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302 if (!port->xmit_buf) {
1303 /* We may sleep in get_zeroed_page() */
1304 unsigned long tmp;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001305
Linus Torvalds1da177e2005-04-16 15:20:36 -07001306 if (!(tmp = get_zeroed_page(GFP_KERNEL))) {
1307 func_exit();
1308 return -ENOMEM;
1309 }
1310
1311 if (port->xmit_buf) {
1312 free_page(tmp);
1313 func_exit();
1314 return -ERESTARTSYS;
1315 }
1316 port->xmit_buf = (unsigned char *) tmp;
1317 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001318
Linus Torvalds1da177e2005-04-16 15:20:36 -07001319 spin_lock_irqsave(&port->lock, flags);
1320
Jeff Garzikd61780c2005-10-30 15:01:51 -08001321 if (port->tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322 clear_bit(TTY_IO_ERROR, &port->tty->flags);
1323
1324 port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
1325 sx_change_speed(bp, port);
1326 port->flags |= ASYNC_INITIALIZED;
1327
1328 spin_unlock_irqrestore(&port->lock, flags);
1329
Jeff Garzikd61780c2005-10-30 15:01:51 -08001330
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331 func_exit();
1332 return 0;
1333}
1334
1335
1336/* Must be called with interrupts disabled */
1337static void sx_shutdown_port(struct specialix_board *bp, struct specialix_port *port)
1338{
1339 struct tty_struct *tty;
1340 int i;
1341 unsigned long flags;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001342
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343 func_enter();
1344
1345 if (!(port->flags & ASYNC_INITIALIZED)) {
1346 func_exit();
1347 return;
1348 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001349
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350 if (sx_debug & SX_DEBUG_FIFO) {
1351 dprintk(SX_DEBUG_FIFO, "sx%d: port %d: %ld overruns, FIFO hits [ ",
1352 board_No(bp), port_No(port), port->overrun);
1353 for (i = 0; i < 10; i++) {
1354 dprintk(SX_DEBUG_FIFO, "%ld ", port->hits[i]);
1355 }
1356 dprintk(SX_DEBUG_FIFO, "].\n");
1357 }
1358
1359 if (port->xmit_buf) {
1360 free_page((unsigned long) port->xmit_buf);
1361 port->xmit_buf = NULL;
1362 }
1363
1364 /* Select port */
1365 spin_lock_irqsave(&bp->lock, flags);
1366 sx_out(bp, CD186x_CAR, port_No(port));
1367
1368 if (!(tty = port->tty) || C_HUPCL(tty)) {
1369 /* Drop DTR */
1370 sx_out(bp, CD186x_MSVDTR, 0);
1371 }
1372 spin_unlock_irqrestore(&bp->lock, flags);
1373 /* Reset port */
1374 sx_wait_CCR(bp);
1375 spin_lock_irqsave(&bp->lock, flags);
1376 sx_out(bp, CD186x_CCR, CCR_SOFTRESET);
1377 /* Disable all interrupts from this port */
1378 port->IER = 0;
1379 sx_out(bp, CD186x_IER, port->IER);
1380 spin_unlock_irqrestore(&bp->lock, flags);
1381 if (tty)
1382 set_bit(TTY_IO_ERROR, &tty->flags);
1383 port->flags &= ~ASYNC_INITIALIZED;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001384
1385 if (!bp->count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001386 sx_shutdown_board(bp);
1387 func_exit();
1388}
1389
Jeff Garzikd61780c2005-10-30 15:01:51 -08001390
Linus Torvalds1da177e2005-04-16 15:20:36 -07001391static int block_til_ready(struct tty_struct *tty, struct file * filp,
1392 struct specialix_port *port)
1393{
1394 DECLARE_WAITQUEUE(wait, current);
1395 struct specialix_board *bp = port_Board(port);
1396 int retval;
1397 int do_clocal = 0;
1398 int CD;
1399 unsigned long flags;
1400
1401 func_enter();
1402
1403 /*
1404 * If the device is in the middle of being closed, then block
1405 * until it's done, and then try again.
1406 */
1407 if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {
1408 interruptible_sleep_on(&port->close_wait);
1409 if (port->flags & ASYNC_HUP_NOTIFY) {
1410 func_exit();
1411 return -EAGAIN;
1412 } else {
1413 func_exit();
1414 return -ERESTARTSYS;
1415 }
1416 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001417
Linus Torvalds1da177e2005-04-16 15:20:36 -07001418 /*
1419 * If non-blocking mode is set, or the port is not enabled,
1420 * then make the check up front and then exit.
1421 */
1422 if ((filp->f_flags & O_NONBLOCK) ||
1423 (tty->flags & (1 << TTY_IO_ERROR))) {
1424 port->flags |= ASYNC_NORMAL_ACTIVE;
1425 func_exit();
1426 return 0;
1427 }
1428
1429 if (C_CLOCAL(tty))
1430 do_clocal = 1;
1431
1432 /*
1433 * Block waiting for the carrier detect and the line to become
1434 * free (i.e., not in use by the callout). While we are in
1435 * this loop, info->count is dropped by one, so that
1436 * rs_close() knows when to free things. We restore it upon
1437 * exit, either normal or abnormal.
1438 */
1439 retval = 0;
1440 add_wait_queue(&port->open_wait, &wait);
1441 spin_lock_irqsave(&port->lock, flags);
1442 if (!tty_hung_up_p(filp)) {
1443 port->count--;
1444 }
1445 spin_unlock_irqrestore(&port->lock, flags);
1446 port->blocked_open++;
1447 while (1) {
1448 spin_lock_irqsave(&bp->lock, flags);
1449 sx_out(bp, CD186x_CAR, port_No(port));
1450 CD = sx_in(bp, CD186x_MSVR) & MSVR_CD;
1451 if (SX_CRTSCTS (tty)) {
1452 /* Activate RTS */
1453 port->MSVR |= MSVR_DTR; /* WTF? */
1454 sx_out (bp, CD186x_MSVR, port->MSVR);
1455 } else {
1456 /* Activate DTR */
1457 port->MSVR |= MSVR_DTR;
1458 sx_out (bp, CD186x_MSVR, port->MSVR);
1459 }
1460 spin_unlock_irqrestore(&bp->lock, flags);
1461 set_current_state(TASK_INTERRUPTIBLE);
1462 if (tty_hung_up_p(filp) ||
1463 !(port->flags & ASYNC_INITIALIZED)) {
1464 if (port->flags & ASYNC_HUP_NOTIFY)
1465 retval = -EAGAIN;
1466 else
Jeff Garzikd61780c2005-10-30 15:01:51 -08001467 retval = -ERESTARTSYS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001468 break;
1469 }
1470 if (!(port->flags & ASYNC_CLOSING) &&
1471 (do_clocal || CD))
1472 break;
1473 if (signal_pending(current)) {
1474 retval = -ERESTARTSYS;
1475 break;
1476 }
1477 schedule();
1478 }
1479
1480 set_current_state(TASK_RUNNING);
1481 remove_wait_queue(&port->open_wait, &wait);
1482 spin_lock_irqsave(&port->lock, flags);
1483 if (!tty_hung_up_p(filp)) {
1484 port->count++;
1485 }
1486 port->blocked_open--;
1487 spin_unlock_irqrestore(&port->lock, flags);
1488 if (retval) {
1489 func_exit();
1490 return retval;
1491 }
1492
1493 port->flags |= ASYNC_NORMAL_ACTIVE;
1494 func_exit();
1495 return 0;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001496}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001497
1498
1499static int sx_open(struct tty_struct * tty, struct file * filp)
1500{
1501 int board;
1502 int error;
1503 struct specialix_port * port;
1504 struct specialix_board * bp;
1505 int i;
1506 unsigned long flags;
1507
1508 func_enter();
1509
1510 board = SX_BOARD(tty->index);
1511
1512 if (board >= SX_NBOARD || !(sx_board[board].flags & SX_BOARD_PRESENT)) {
1513 func_exit();
1514 return -ENODEV;
1515 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001516
Linus Torvalds1da177e2005-04-16 15:20:36 -07001517 bp = &sx_board[board];
1518 port = sx_port + board * SX_NPORT + SX_PORT(tty->index);
1519 port->overrun = 0;
1520 for (i = 0; i < 10; i++)
1521 port->hits[i]=0;
1522
1523 dprintk (SX_DEBUG_OPEN, "Board = %d, bp = %p, port = %p, portno = %d.\n",
1524 board, bp, port, SX_PORT(tty->index));
1525
1526 if (sx_paranoia_check(port, tty->name, "sx_open")) {
1527 func_enter();
1528 return -ENODEV;
1529 }
1530
1531 if ((error = sx_setup_board(bp))) {
1532 func_exit();
1533 return error;
1534 }
1535
1536 spin_lock_irqsave(&bp->lock, flags);
1537 port->count++;
1538 bp->count++;
1539 tty->driver_data = port;
1540 port->tty = tty;
1541 spin_unlock_irqrestore(&bp->lock, flags);
1542
1543 if ((error = sx_setup_port(bp, port))) {
1544 func_enter();
1545 return error;
1546 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001547
Linus Torvalds1da177e2005-04-16 15:20:36 -07001548 if ((error = block_til_ready(tty, filp, port))) {
1549 func_enter();
1550 return error;
1551 }
1552
1553 func_exit();
1554 return 0;
1555}
1556
1557
1558static void sx_close(struct tty_struct * tty, struct file * filp)
1559{
1560 struct specialix_port *port = (struct specialix_port *) tty->driver_data;
1561 struct specialix_board *bp;
1562 unsigned long flags;
1563 unsigned long timeout;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001564
Linus Torvalds1da177e2005-04-16 15:20:36 -07001565 func_enter();
1566 if (!port || sx_paranoia_check(port, tty->name, "close")) {
1567 func_exit();
1568 return;
1569 }
1570 spin_lock_irqsave(&port->lock, flags);
1571
1572 if (tty_hung_up_p(filp)) {
1573 spin_unlock_irqrestore(&port->lock, flags);
1574 func_exit();
1575 return;
1576 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001577
Linus Torvalds1da177e2005-04-16 15:20:36 -07001578 bp = port_Board(port);
1579 if ((tty->count == 1) && (port->count != 1)) {
1580 printk(KERN_ERR "sx%d: sx_close: bad port count;"
1581 " tty->count is 1, port count is %d\n",
1582 board_No(bp), port->count);
1583 port->count = 1;
1584 }
1585
1586 if (port->count > 1) {
1587 port->count--;
1588 bp->count--;
1589
1590 spin_unlock_irqrestore(&port->lock, flags);
1591
1592 func_exit();
1593 return;
1594 }
1595 port->flags |= ASYNC_CLOSING;
1596 /*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001597 * Now we wait for the transmit buffer to clear; and we notify
Linus Torvalds1da177e2005-04-16 15:20:36 -07001598 * the line discipline to only process XON/XOFF characters.
1599 */
1600 tty->closing = 1;
1601 spin_unlock_irqrestore(&port->lock, flags);
1602 dprintk (SX_DEBUG_OPEN, "Closing\n");
1603 if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) {
1604 tty_wait_until_sent(tty, port->closing_wait);
1605 }
1606 /*
1607 * At this point we stop accepting input. To do this, we
1608 * disable the receive line status interrupts, and tell the
1609 * interrupt driver to stop checking the data ready bit in the
1610 * line status register.
1611 */
1612 dprintk (SX_DEBUG_OPEN, "Closed\n");
1613 port->IER &= ~IER_RXD;
1614 if (port->flags & ASYNC_INITIALIZED) {
1615 port->IER &= ~IER_TXRDY;
1616 port->IER |= IER_TXEMPTY;
1617 spin_lock_irqsave(&bp->lock, flags);
1618 sx_out(bp, CD186x_CAR, port_No(port));
1619 sx_out(bp, CD186x_IER, port->IER);
1620 spin_unlock_irqrestore(&bp->lock, flags);
1621 /*
1622 * Before we drop DTR, make sure the UART transmitter
1623 * has completely drained; this is especially
1624 * important if there is a transmit FIFO!
1625 */
1626 timeout = jiffies+HZ;
1627 while(port->IER & IER_TXEMPTY) {
1628 set_current_state (TASK_INTERRUPTIBLE);
1629 msleep_interruptible(jiffies_to_msecs(port->timeout));
1630 if (time_after(jiffies, timeout)) {
1631 printk (KERN_INFO "Timeout waiting for close\n");
1632 break;
1633 }
1634 }
1635
1636 }
1637
1638 if (--bp->count < 0) {
1639 printk(KERN_ERR "sx%d: sx_shutdown_port: bad board count: %d port: %d\n",
1640 board_No(bp), bp->count, tty->index);
1641 bp->count = 0;
1642 }
1643 if (--port->count < 0) {
1644 printk(KERN_ERR "sx%d: sx_close: bad port count for tty%d: %d\n",
1645 board_No(bp), port_No(port), port->count);
1646 port->count = 0;
1647 }
1648
1649 sx_shutdown_port(bp, port);
1650 if (tty->driver->flush_buffer)
1651 tty->driver->flush_buffer(tty);
1652 tty_ldisc_flush(tty);
1653 spin_lock_irqsave(&port->lock, flags);
1654 tty->closing = 0;
1655 port->event = 0;
1656 port->tty = NULL;
1657 spin_unlock_irqrestore(&port->lock, flags);
1658 if (port->blocked_open) {
1659 if (port->close_delay) {
1660 msleep_interruptible(jiffies_to_msecs(port->close_delay));
1661 }
1662 wake_up_interruptible(&port->open_wait);
1663 }
1664 port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
1665 wake_up_interruptible(&port->close_wait);
1666
1667 func_exit();
1668}
1669
1670
Jeff Garzikd61780c2005-10-30 15:01:51 -08001671static int sx_write(struct tty_struct * tty,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001672 const unsigned char *buf, int count)
1673{
1674 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1675 struct specialix_board *bp;
1676 int c, total = 0;
1677 unsigned long flags;
1678
1679 func_enter();
1680 if (sx_paranoia_check(port, tty->name, "sx_write")) {
1681 func_exit();
1682 return 0;
1683 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001684
Linus Torvalds1da177e2005-04-16 15:20:36 -07001685 bp = port_Board(port);
1686
1687 if (!tty || !port->xmit_buf || !tmp_buf) {
1688 func_exit();
1689 return 0;
1690 }
1691
1692 while (1) {
1693 spin_lock_irqsave(&port->lock, flags);
1694 c = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
1695 SERIAL_XMIT_SIZE - port->xmit_head));
1696 if (c <= 0) {
1697 spin_unlock_irqrestore(&port->lock, flags);
1698 break;
1699 }
1700 memcpy(port->xmit_buf + port->xmit_head, buf, c);
1701 port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
1702 port->xmit_cnt += c;
1703 spin_unlock_irqrestore(&port->lock, flags);
1704
1705 buf += c;
1706 count -= c;
1707 total += c;
1708 }
1709
1710 spin_lock_irqsave(&bp->lock, flags);
1711 if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
1712 !(port->IER & IER_TXRDY)) {
1713 port->IER |= IER_TXRDY;
1714 sx_out(bp, CD186x_CAR, port_No(port));
1715 sx_out(bp, CD186x_IER, port->IER);
1716 }
1717 spin_unlock_irqrestore(&bp->lock, flags);
1718 func_exit();
1719
1720 return total;
1721}
1722
1723
1724static void sx_put_char(struct tty_struct * tty, unsigned char ch)
1725{
1726 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1727 unsigned long flags;
1728 struct specialix_board * bp;
1729
1730 func_enter();
1731
1732 if (sx_paranoia_check(port, tty->name, "sx_put_char")) {
1733 func_exit();
1734 return;
1735 }
1736 dprintk (SX_DEBUG_TX, "check tty: %p %p\n", tty, port->xmit_buf);
1737 if (!tty || !port->xmit_buf) {
1738 func_exit();
1739 return;
1740 }
1741 bp = port_Board(port);
1742 spin_lock_irqsave(&port->lock, flags);
1743
1744 dprintk (SX_DEBUG_TX, "xmit_cnt: %d xmit_buf: %p\n", port->xmit_cnt, port->xmit_buf);
1745 if ((port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) || (!port->xmit_buf)) {
1746 spin_unlock_irqrestore(&port->lock, flags);
1747 dprintk (SX_DEBUG_TX, "Exit size\n");
1748 func_exit();
1749 return;
1750 }
1751 dprintk (SX_DEBUG_TX, "Handle xmit: %p %p\n", port, port->xmit_buf);
1752 port->xmit_buf[port->xmit_head++] = ch;
1753 port->xmit_head &= SERIAL_XMIT_SIZE - 1;
1754 port->xmit_cnt++;
1755 spin_unlock_irqrestore(&port->lock, flags);
1756
1757 func_exit();
1758}
1759
1760
1761static void sx_flush_chars(struct tty_struct * tty)
1762{
1763 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1764 unsigned long flags;
1765 struct specialix_board * bp = port_Board(port);
1766
1767 func_enter();
1768
1769 if (sx_paranoia_check(port, tty->name, "sx_flush_chars")) {
1770 func_exit();
1771 return;
1772 }
1773 if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
1774 !port->xmit_buf) {
1775 func_exit();
1776 return;
1777 }
1778 spin_lock_irqsave(&bp->lock, flags);
1779 port->IER |= IER_TXRDY;
1780 sx_out(port_Board(port), CD186x_CAR, port_No(port));
1781 sx_out(port_Board(port), CD186x_IER, port->IER);
1782 spin_unlock_irqrestore(&bp->lock, flags);
1783
1784 func_exit();
1785}
1786
1787
1788static int sx_write_room(struct tty_struct * tty)
1789{
1790 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1791 int ret;
1792
1793 func_enter();
1794
1795 if (sx_paranoia_check(port, tty->name, "sx_write_room")) {
1796 func_exit();
1797 return 0;
1798 }
1799
1800 ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;
1801 if (ret < 0)
1802 ret = 0;
1803
1804 func_exit();
1805 return ret;
1806}
1807
1808
1809static int sx_chars_in_buffer(struct tty_struct *tty)
1810{
1811 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1812
1813 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08001814
Linus Torvalds1da177e2005-04-16 15:20:36 -07001815 if (sx_paranoia_check(port, tty->name, "sx_chars_in_buffer")) {
1816 func_exit();
1817 return 0;
1818 }
1819 func_exit();
1820 return port->xmit_cnt;
1821}
1822
1823
1824static void sx_flush_buffer(struct tty_struct *tty)
1825{
1826 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1827 unsigned long flags;
1828 struct specialix_board * bp;
1829
1830 func_enter();
1831
1832 if (sx_paranoia_check(port, tty->name, "sx_flush_buffer")) {
1833 func_exit();
1834 return;
1835 }
1836
1837 bp = port_Board(port);
1838 spin_lock_irqsave(&port->lock, flags);
1839 port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
1840 spin_unlock_irqrestore(&port->lock, flags);
1841 tty_wakeup(tty);
1842
1843 func_exit();
1844}
1845
1846
1847static int sx_tiocmget(struct tty_struct *tty, struct file *file)
1848{
1849 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1850 struct specialix_board * bp;
1851 unsigned char status;
1852 unsigned int result;
1853 unsigned long flags;
1854
1855 func_enter();
1856
1857 if (sx_paranoia_check(port, tty->name, __FUNCTION__)) {
1858 func_exit();
1859 return -ENODEV;
1860 }
1861
1862 bp = port_Board(port);
1863 spin_lock_irqsave (&bp->lock, flags);
1864 sx_out(bp, CD186x_CAR, port_No(port));
1865 status = sx_in(bp, CD186x_MSVR);
1866 spin_unlock_irqrestore(&bp->lock, flags);
1867 dprintk (SX_DEBUG_INIT, "Got msvr[%d] = %02x, car = %d.\n",
1868 port_No(port), status, sx_in (bp, CD186x_CAR));
1869 dprintk (SX_DEBUG_INIT, "sx_port = %p, port = %p\n", sx_port, port);
1870 if (SX_CRTSCTS(port->tty)) {
Jeff Garzikd61780c2005-10-30 15:01:51 -08001871 result = /* (status & MSVR_RTS) ? */ TIOCM_DTR /* : 0) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001872 | ((status & MSVR_DTR) ? TIOCM_RTS : 0)
1873 | ((status & MSVR_CD) ? TIOCM_CAR : 0)
1874 |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */
1875 | ((status & MSVR_CTS) ? TIOCM_CTS : 0);
1876 } else {
Jeff Garzikd61780c2005-10-30 15:01:51 -08001877 result = /* (status & MSVR_RTS) ? */ TIOCM_RTS /* : 0) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001878 | ((status & MSVR_DTR) ? TIOCM_DTR : 0)
1879 | ((status & MSVR_CD) ? TIOCM_CAR : 0)
1880 |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */
1881 | ((status & MSVR_CTS) ? TIOCM_CTS : 0);
1882 }
1883
1884 func_exit();
1885
1886 return result;
1887}
1888
1889
1890static int sx_tiocmset(struct tty_struct *tty, struct file *file,
1891 unsigned int set, unsigned int clear)
1892{
1893 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1894 unsigned long flags;
1895 struct specialix_board *bp;
1896
1897 func_enter();
1898
1899 if (sx_paranoia_check(port, tty->name, __FUNCTION__)) {
1900 func_exit();
1901 return -ENODEV;
1902 }
1903
1904 bp = port_Board(port);
1905
1906 spin_lock_irqsave(&port->lock, flags);
1907 /* if (set & TIOCM_RTS)
1908 port->MSVR |= MSVR_RTS; */
1909 /* if (set & TIOCM_DTR)
1910 port->MSVR |= MSVR_DTR; */
1911
1912 if (SX_CRTSCTS(port->tty)) {
1913 if (set & TIOCM_RTS)
1914 port->MSVR |= MSVR_DTR;
1915 } else {
1916 if (set & TIOCM_DTR)
1917 port->MSVR |= MSVR_DTR;
1918 }
1919
1920 /* if (clear & TIOCM_RTS)
1921 port->MSVR &= ~MSVR_RTS; */
1922 /* if (clear & TIOCM_DTR)
1923 port->MSVR &= ~MSVR_DTR; */
1924 if (SX_CRTSCTS(port->tty)) {
1925 if (clear & TIOCM_RTS)
1926 port->MSVR &= ~MSVR_DTR;
1927 } else {
1928 if (clear & TIOCM_DTR)
1929 port->MSVR &= ~MSVR_DTR;
1930 }
1931 spin_lock_irqsave(&bp->lock, flags);
1932 sx_out(bp, CD186x_CAR, port_No(port));
1933 sx_out(bp, CD186x_MSVR, port->MSVR);
1934 spin_unlock_irqrestore(&bp->lock, flags);
1935 spin_unlock_irqrestore(&port->lock, flags);
1936 func_exit();
1937 return 0;
1938}
1939
1940
1941static inline void sx_send_break(struct specialix_port * port, unsigned long length)
1942{
1943 struct specialix_board *bp = port_Board(port);
1944 unsigned long flags;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001945
Linus Torvalds1da177e2005-04-16 15:20:36 -07001946 func_enter();
1947
1948 spin_lock_irqsave (&port->lock, flags);
1949 port->break_length = SPECIALIX_TPS / HZ * length;
1950 port->COR2 |= COR2_ETC;
1951 port->IER |= IER_TXRDY;
1952 spin_lock_irqsave(&bp->lock, flags);
1953 sx_out(bp, CD186x_CAR, port_No(port));
1954 sx_out(bp, CD186x_COR2, port->COR2);
1955 sx_out(bp, CD186x_IER, port->IER);
1956 spin_unlock_irqrestore(&bp->lock, flags);
1957 spin_unlock_irqrestore (&port->lock, flags);
1958 sx_wait_CCR(bp);
1959 spin_lock_irqsave(&bp->lock, flags);
1960 sx_out(bp, CD186x_CCR, CCR_CORCHG2);
1961 spin_unlock_irqrestore(&bp->lock, flags);
1962 sx_wait_CCR(bp);
1963
1964 func_exit();
1965}
1966
1967
1968static inline int sx_set_serial_info(struct specialix_port * port,
1969 struct serial_struct __user * newinfo)
1970{
1971 struct serial_struct tmp;
1972 struct specialix_board *bp = port_Board(port);
1973 int change_speed;
1974
1975 func_enter();
1976 /*
Jesper Juhle49332b2005-05-01 08:59:08 -07001977 if (!access_ok(VERIFY_READ, (void *) newinfo, sizeof(tmp))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001978 func_exit();
Jesper Juhle49332b2005-05-01 08:59:08 -07001979 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001980 }
1981 */
1982 if (copy_from_user(&tmp, newinfo, sizeof(tmp))) {
1983 func_enter();
1984 return -EFAULT;
1985 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001986
1987#if 0
Linus Torvalds1da177e2005-04-16 15:20:36 -07001988 if ((tmp.irq != bp->irq) ||
1989 (tmp.port != bp->base) ||
1990 (tmp.type != PORT_CIRRUS) ||
1991 (tmp.baud_base != (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC) ||
1992 (tmp.custom_divisor != 0) ||
1993 (tmp.xmit_fifo_size != CD186x_NFIFO) ||
1994 (tmp.flags & ~SPECIALIX_LEGAL_FLAGS)) {
1995 func_exit();
1996 return -EINVAL;
1997 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001998#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001999
2000 change_speed = ((port->flags & ASYNC_SPD_MASK) !=
2001 (tmp.flags & ASYNC_SPD_MASK));
2002 change_speed |= (tmp.custom_divisor != port->custom_divisor);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002003
Linus Torvalds1da177e2005-04-16 15:20:36 -07002004 if (!capable(CAP_SYS_ADMIN)) {
2005 if ((tmp.close_delay != port->close_delay) ||
2006 (tmp.closing_wait != port->closing_wait) ||
2007 ((tmp.flags & ~ASYNC_USR_MASK) !=
2008 (port->flags & ~ASYNC_USR_MASK))) {
2009 func_exit();
2010 return -EPERM;
2011 }
2012 port->flags = ((port->flags & ~ASYNC_USR_MASK) |
2013 (tmp.flags & ASYNC_USR_MASK));
2014 port->custom_divisor = tmp.custom_divisor;
2015 } else {
2016 port->flags = ((port->flags & ~ASYNC_FLAGS) |
2017 (tmp.flags & ASYNC_FLAGS));
2018 port->close_delay = tmp.close_delay;
2019 port->closing_wait = tmp.closing_wait;
2020 port->custom_divisor = tmp.custom_divisor;
2021 }
2022 if (change_speed) {
2023 sx_change_speed(bp, port);
2024 }
2025 func_exit();
2026 return 0;
2027}
2028
2029
2030static inline int sx_get_serial_info(struct specialix_port * port,
2031 struct serial_struct __user *retinfo)
2032{
2033 struct serial_struct tmp;
2034 struct specialix_board *bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002035
Linus Torvalds1da177e2005-04-16 15:20:36 -07002036 func_enter();
2037
2038 /*
Jesper Juhle49332b2005-05-01 08:59:08 -07002039 if (!access_ok(VERIFY_WRITE, (void *) retinfo, sizeof(tmp)))
2040 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002041 */
2042
2043 memset(&tmp, 0, sizeof(tmp));
2044 tmp.type = PORT_CIRRUS;
2045 tmp.line = port - sx_port;
2046 tmp.port = bp->base;
2047 tmp.irq = bp->irq;
2048 tmp.flags = port->flags;
2049 tmp.baud_base = (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC;
2050 tmp.close_delay = port->close_delay * HZ/100;
2051 tmp.closing_wait = port->closing_wait * HZ/100;
2052 tmp.custom_divisor = port->custom_divisor;
2053 tmp.xmit_fifo_size = CD186x_NFIFO;
2054 if (copy_to_user(retinfo, &tmp, sizeof(tmp))) {
2055 func_exit();
2056 return -EFAULT;
2057 }
2058
2059 func_exit();
2060 return 0;
2061}
2062
2063
Jeff Garzikd61780c2005-10-30 15:01:51 -08002064static int sx_ioctl(struct tty_struct * tty, struct file * filp,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002065 unsigned int cmd, unsigned long arg)
2066{
2067 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2068 int retval;
2069 void __user *argp = (void __user *)arg;
2070
2071 func_enter();
2072
2073 if (sx_paranoia_check(port, tty->name, "sx_ioctl")) {
2074 func_exit();
2075 return -ENODEV;
2076 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002077
Linus Torvalds1da177e2005-04-16 15:20:36 -07002078 switch (cmd) {
2079 case TCSBRK: /* SVID version: non-zero arg --> no break */
2080 retval = tty_check_change(tty);
2081 if (retval) {
2082 func_exit();
2083 return retval;
2084 }
2085 tty_wait_until_sent(tty, 0);
2086 if (!arg)
2087 sx_send_break(port, HZ/4); /* 1/4 second */
2088 return 0;
2089 case TCSBRKP: /* support for POSIX tcsendbreak() */
2090 retval = tty_check_change(tty);
2091 if (retval) {
2092 func_exit();
2093 return retval;
2094 }
2095 tty_wait_until_sent(tty, 0);
2096 sx_send_break(port, arg ? arg*(HZ/10) : HZ/4);
2097 func_exit();
2098 return 0;
2099 case TIOCGSOFTCAR:
2100 if (put_user(C_CLOCAL(tty)?1:0, (unsigned long __user *)argp)) {
2101 func_exit();
2102 return -EFAULT;
2103 }
2104 func_exit();
2105 return 0;
2106 case TIOCSSOFTCAR:
2107 if (get_user(arg, (unsigned long __user *) argp)) {
2108 func_exit();
2109 return -EFAULT;
2110 }
2111 tty->termios->c_cflag =
2112 ((tty->termios->c_cflag & ~CLOCAL) |
2113 (arg ? CLOCAL : 0));
2114 func_exit();
2115 return 0;
2116 case TIOCGSERIAL:
2117 func_exit();
2118 return sx_get_serial_info(port, argp);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002119 case TIOCSSERIAL:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002120 func_exit();
2121 return sx_set_serial_info(port, argp);
2122 default:
2123 func_exit();
2124 return -ENOIOCTLCMD;
2125 }
2126 func_exit();
2127 return 0;
2128}
2129
2130
2131static void sx_throttle(struct tty_struct * tty)
2132{
2133 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2134 struct specialix_board *bp;
2135 unsigned long flags;
2136
2137 func_enter();
2138
2139 if (sx_paranoia_check(port, tty->name, "sx_throttle")) {
2140 func_exit();
2141 return;
2142 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002143
Linus Torvalds1da177e2005-04-16 15:20:36 -07002144 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002145
Linus Torvalds1da177e2005-04-16 15:20:36 -07002146 /* Use DTR instead of RTS ! */
Jeff Garzikd61780c2005-10-30 15:01:51 -08002147 if (SX_CRTSCTS (tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002148 port->MSVR &= ~MSVR_DTR;
2149 else {
2150 /* Auch!!! I think the system shouldn't call this then. */
2151 /* Or maybe we're supposed (allowed?) to do our side of hw
Jeff Garzikd61780c2005-10-30 15:01:51 -08002152 handshake anyway, even when hardware handshake is off.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002153 When you see this in your logs, please report.... */
2154 printk (KERN_ERR "sx%d: Need to throttle, but can't (hardware hs is off)\n",
2155 port_No (port));
2156 }
2157 spin_lock_irqsave(&bp->lock, flags);
2158 sx_out(bp, CD186x_CAR, port_No(port));
2159 spin_unlock_irqrestore(&bp->lock, flags);
2160 if (I_IXOFF(tty)) {
2161 spin_unlock_irqrestore(&bp->lock, flags);
2162 sx_wait_CCR(bp);
2163 spin_lock_irqsave(&bp->lock, flags);
2164 sx_out(bp, CD186x_CCR, CCR_SSCH2);
2165 spin_unlock_irqrestore(&bp->lock, flags);
2166 sx_wait_CCR(bp);
2167 }
2168 spin_lock_irqsave(&bp->lock, flags);
2169 sx_out(bp, CD186x_MSVR, port->MSVR);
2170 spin_unlock_irqrestore(&bp->lock, flags);
2171
2172 func_exit();
2173}
2174
2175
2176static void sx_unthrottle(struct tty_struct * tty)
2177{
2178 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2179 struct specialix_board *bp;
2180 unsigned long flags;
2181
2182 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002183
Linus Torvalds1da177e2005-04-16 15:20:36 -07002184 if (sx_paranoia_check(port, tty->name, "sx_unthrottle")) {
2185 func_exit();
2186 return;
2187 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002188
Linus Torvalds1da177e2005-04-16 15:20:36 -07002189 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002190
Linus Torvalds1da177e2005-04-16 15:20:36 -07002191 spin_lock_irqsave(&port->lock, flags);
2192 /* XXXX Use DTR INSTEAD???? */
2193 if (SX_CRTSCTS(tty)) {
2194 port->MSVR |= MSVR_DTR;
2195 } /* Else clause: see remark in "sx_throttle"... */
2196 spin_lock_irqsave(&bp->lock, flags);
2197 sx_out(bp, CD186x_CAR, port_No(port));
2198 spin_unlock_irqrestore(&bp->lock, flags);
2199 if (I_IXOFF(tty)) {
2200 spin_unlock_irqrestore(&port->lock, flags);
2201 sx_wait_CCR(bp);
2202 spin_lock_irqsave(&bp->lock, flags);
2203 sx_out(bp, CD186x_CCR, CCR_SSCH1);
2204 spin_unlock_irqrestore(&bp->lock, flags);
2205 sx_wait_CCR(bp);
2206 spin_lock_irqsave(&port->lock, flags);
2207 }
2208 spin_lock_irqsave(&bp->lock, flags);
2209 sx_out(bp, CD186x_MSVR, port->MSVR);
2210 spin_unlock_irqrestore(&bp->lock, flags);
2211 spin_unlock_irqrestore(&port->lock, flags);
2212
2213 func_exit();
2214}
2215
2216
2217static void sx_stop(struct tty_struct * tty)
2218{
2219 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2220 struct specialix_board *bp;
2221 unsigned long flags;
2222
2223 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002224
Linus Torvalds1da177e2005-04-16 15:20:36 -07002225 if (sx_paranoia_check(port, tty->name, "sx_stop")) {
2226 func_exit();
2227 return;
2228 }
2229
2230 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002231
Linus Torvalds1da177e2005-04-16 15:20:36 -07002232 spin_lock_irqsave(&port->lock, flags);
2233 port->IER &= ~IER_TXRDY;
2234 spin_lock_irqsave(&bp->lock, flags);
2235 sx_out(bp, CD186x_CAR, port_No(port));
2236 sx_out(bp, CD186x_IER, port->IER);
2237 spin_unlock_irqrestore(&bp->lock, flags);
2238 spin_unlock_irqrestore(&port->lock, flags);
2239
2240 func_exit();
2241}
2242
2243
2244static void sx_start(struct tty_struct * tty)
2245{
2246 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2247 struct specialix_board *bp;
2248 unsigned long flags;
2249
2250 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002251
Linus Torvalds1da177e2005-04-16 15:20:36 -07002252 if (sx_paranoia_check(port, tty->name, "sx_start")) {
2253 func_exit();
2254 return;
2255 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002256
Linus Torvalds1da177e2005-04-16 15:20:36 -07002257 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002258
Linus Torvalds1da177e2005-04-16 15:20:36 -07002259 spin_lock_irqsave(&port->lock, flags);
2260 if (port->xmit_cnt && port->xmit_buf && !(port->IER & IER_TXRDY)) {
2261 port->IER |= IER_TXRDY;
2262 spin_lock_irqsave(&bp->lock, flags);
2263 sx_out(bp, CD186x_CAR, port_No(port));
2264 sx_out(bp, CD186x_IER, port->IER);
2265 spin_unlock_irqrestore(&bp->lock, flags);
2266 }
2267 spin_unlock_irqrestore(&port->lock, flags);
2268
2269 func_exit();
2270}
2271
2272
2273/*
2274 * This routine is called from the work-queue when the interrupt
2275 * routine has signalled that a hangup has occurred. The path of
2276 * hangup processing is:
2277 *
2278 * serial interrupt routine -> (workqueue) ->
2279 * do_sx_hangup() -> tty->hangup() -> sx_hangup()
Jeff Garzikd61780c2005-10-30 15:01:51 -08002280 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07002281 */
2282static void do_sx_hangup(void *private_)
2283{
2284 struct specialix_port *port = (struct specialix_port *) private_;
2285 struct tty_struct *tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002286
Linus Torvalds1da177e2005-04-16 15:20:36 -07002287 func_enter();
2288
2289 tty = port->tty;
2290 if (tty)
2291 tty_hangup(tty); /* FIXME: module removal race here */
2292
2293 func_exit();
2294}
2295
2296
2297static void sx_hangup(struct tty_struct * tty)
2298{
2299 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2300 struct specialix_board *bp;
2301 unsigned long flags;
2302
2303 func_enter();
2304
2305 if (sx_paranoia_check(port, tty->name, "sx_hangup")) {
2306 func_exit();
2307 return;
2308 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002309
Linus Torvalds1da177e2005-04-16 15:20:36 -07002310 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002311
Linus Torvalds1da177e2005-04-16 15:20:36 -07002312 sx_shutdown_port(bp, port);
2313 spin_lock_irqsave(&port->lock, flags);
2314 port->event = 0;
2315 bp->count -= port->count;
2316 if (bp->count < 0) {
2317 printk(KERN_ERR "sx%d: sx_hangup: bad board count: %d port: %d\n",
2318 board_No(bp), bp->count, tty->index);
2319 bp->count = 0;
2320 }
2321 port->count = 0;
2322 port->flags &= ~ASYNC_NORMAL_ACTIVE;
2323 port->tty = NULL;
2324 spin_unlock_irqrestore(&port->lock, flags);
2325 wake_up_interruptible(&port->open_wait);
2326
2327 func_exit();
2328}
2329
2330
2331static void sx_set_termios(struct tty_struct * tty, struct termios * old_termios)
2332{
2333 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2334 unsigned long flags;
2335 struct specialix_board * bp;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002336
Linus Torvalds1da177e2005-04-16 15:20:36 -07002337 if (sx_paranoia_check(port, tty->name, "sx_set_termios"))
2338 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002339
Linus Torvalds1da177e2005-04-16 15:20:36 -07002340 if (tty->termios->c_cflag == old_termios->c_cflag &&
2341 tty->termios->c_iflag == old_termios->c_iflag)
2342 return;
2343
2344 bp = port_Board(port);
2345 spin_lock_irqsave(&port->lock, flags);
2346 sx_change_speed(port_Board(port), port);
2347 spin_unlock_irqrestore(&port->lock, flags);
2348
2349 if ((old_termios->c_cflag & CRTSCTS) &&
2350 !(tty->termios->c_cflag & CRTSCTS)) {
2351 tty->hw_stopped = 0;
2352 sx_start(tty);
2353 }
2354}
2355
2356
2357static void do_softint(void *private_)
2358{
2359 struct specialix_port *port = (struct specialix_port *) private_;
2360 struct tty_struct *tty;
2361
2362 func_enter();
2363
2364 if(!(tty = port->tty)) {
2365 func_exit();
2366 return;
2367 }
2368
2369 if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) {
2370 tty_wakeup(tty);
2371 //wake_up_interruptible(&tty->write_wait);
2372 }
2373
2374 func_exit();
2375}
2376
2377static struct tty_operations sx_ops = {
2378 .open = sx_open,
2379 .close = sx_close,
2380 .write = sx_write,
2381 .put_char = sx_put_char,
2382 .flush_chars = sx_flush_chars,
2383 .write_room = sx_write_room,
2384 .chars_in_buffer = sx_chars_in_buffer,
2385 .flush_buffer = sx_flush_buffer,
2386 .ioctl = sx_ioctl,
2387 .throttle = sx_throttle,
2388 .unthrottle = sx_unthrottle,
2389 .set_termios = sx_set_termios,
2390 .stop = sx_stop,
2391 .start = sx_start,
2392 .hangup = sx_hangup,
2393 .tiocmget = sx_tiocmget,
2394 .tiocmset = sx_tiocmset,
2395};
2396
2397static int sx_init_drivers(void)
2398{
2399 int error;
2400 int i;
2401
2402 func_enter();
2403
2404 specialix_driver = alloc_tty_driver(SX_NBOARD * SX_NPORT);
2405 if (!specialix_driver) {
2406 printk(KERN_ERR "sx: Couldn't allocate tty_driver.\n");
2407 func_exit();
2408 return 1;
2409 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002410
Linus Torvalds1da177e2005-04-16 15:20:36 -07002411 if (!(tmp_buf = (unsigned char *) get_zeroed_page(GFP_KERNEL))) {
2412 printk(KERN_ERR "sx: Couldn't get free page.\n");
2413 put_tty_driver(specialix_driver);
2414 func_exit();
2415 return 1;
2416 }
2417 specialix_driver->owner = THIS_MODULE;
2418 specialix_driver->name = "ttyW";
2419 specialix_driver->major = SPECIALIX_NORMAL_MAJOR;
2420 specialix_driver->type = TTY_DRIVER_TYPE_SERIAL;
2421 specialix_driver->subtype = SERIAL_TYPE_NORMAL;
2422 specialix_driver->init_termios = tty_std_termios;
2423 specialix_driver->init_termios.c_cflag =
2424 B9600 | CS8 | CREAD | HUPCL | CLOCAL;
2425 specialix_driver->flags = TTY_DRIVER_REAL_RAW;
2426 tty_set_operations(specialix_driver, &sx_ops);
2427
2428 if ((error = tty_register_driver(specialix_driver))) {
2429 put_tty_driver(specialix_driver);
2430 free_page((unsigned long)tmp_buf);
2431 printk(KERN_ERR "sx: Couldn't register specialix IO8+ driver, error = %d\n",
2432 error);
2433 func_exit();
2434 return 1;
2435 }
2436 memset(sx_port, 0, sizeof(sx_port));
2437 for (i = 0; i < SX_NPORT * SX_NBOARD; i++) {
2438 sx_port[i].magic = SPECIALIX_MAGIC;
2439 INIT_WORK(&sx_port[i].tqueue, do_softint, &sx_port[i]);
2440 INIT_WORK(&sx_port[i].tqueue_hangup, do_sx_hangup, &sx_port[i]);
2441 sx_port[i].close_delay = 50 * HZ/100;
2442 sx_port[i].closing_wait = 3000 * HZ/100;
2443 init_waitqueue_head(&sx_port[i].open_wait);
2444 init_waitqueue_head(&sx_port[i].close_wait);
2445 spin_lock_init(&sx_port[i].lock);
2446 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002447
Linus Torvalds1da177e2005-04-16 15:20:36 -07002448 func_exit();
2449 return 0;
2450}
2451
2452static void sx_release_drivers(void)
2453{
2454 func_enter();
2455
2456 free_page((unsigned long)tmp_buf);
2457 tty_unregister_driver(specialix_driver);
2458 put_tty_driver(specialix_driver);
2459 func_exit();
2460}
2461
Jeff Garzikd61780c2005-10-30 15:01:51 -08002462/*
2463 * This routine must be called by kernel at boot time
Linus Torvalds1da177e2005-04-16 15:20:36 -07002464 */
2465static int __init specialix_init(void)
2466{
2467 int i;
2468 int found = 0;
2469
2470 func_enter();
2471
2472 printk(KERN_INFO "sx: Specialix IO8+ driver v" VERSION ", (c) R.E.Wolff 1997/1998.\n");
2473 printk(KERN_INFO "sx: derived from work (c) D.Gorodchanin 1994-1996.\n");
2474#ifdef CONFIG_SPECIALIX_RTSCTS
2475 printk (KERN_INFO "sx: DTR/RTS pin is always RTS.\n");
2476#else
2477 printk (KERN_INFO "sx: DTR/RTS pin is RTS when CRTSCTS is on.\n");
2478#endif
Jeff Garzikd61780c2005-10-30 15:01:51 -08002479
Linus Torvalds1da177e2005-04-16 15:20:36 -07002480 for (i = 0; i < SX_NBOARD; i++)
2481 sx_board[i].lock = SPIN_LOCK_UNLOCKED;
2482
2483 if (sx_init_drivers()) {
2484 func_exit();
2485 return -EIO;
2486 }
2487
Jeff Garzikd61780c2005-10-30 15:01:51 -08002488 for (i = 0; i < SX_NBOARD; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002489 if (sx_board[i].base && !sx_probe(&sx_board[i]))
2490 found++;
2491
2492#ifdef CONFIG_PCI
2493 {
2494 struct pci_dev *pdev = NULL;
2495
2496 i=0;
2497 while (i < SX_NBOARD) {
2498 if (sx_board[i].flags & SX_BOARD_PRESENT) {
2499 i++;
2500 continue;
2501 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002502 pdev = pci_find_device (PCI_VENDOR_ID_SPECIALIX,
2503 PCI_DEVICE_ID_SPECIALIX_IO8,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002504 pdev);
2505 if (!pdev) break;
2506
2507 if (pci_enable_device(pdev))
2508 continue;
2509
2510 sx_board[i].irq = pdev->irq;
2511
2512 sx_board[i].base = pci_resource_start (pdev, 2);
2513
2514 sx_board[i].flags |= SX_BOARD_IS_PCI;
2515 if (!sx_probe(&sx_board[i]))
2516 found ++;
2517 }
2518 }
2519#endif
2520
2521 if (!found) {
2522 sx_release_drivers();
2523 printk(KERN_INFO "sx: No specialix IO8+ boards detected.\n");
2524 func_exit();
2525 return -EIO;
2526 }
2527
2528 func_exit();
2529 return 0;
2530}
2531
2532static int iobase[SX_NBOARD] = {0,};
2533
2534static int irq [SX_NBOARD] = {0,};
2535
2536module_param_array(iobase, int, NULL, 0);
2537module_param_array(irq, int, NULL, 0);
2538module_param(sx_debug, int, 0);
2539module_param(sx_rxfifo, int, 0);
2540#ifdef SPECIALIX_TIMER
2541module_param(sx_poll, int, 0);
2542#endif
2543
2544/*
2545 * You can setup up to 4 boards.
2546 * by specifying "iobase=0xXXX,0xXXX ..." as insmod parameter.
Jeff Garzikd61780c2005-10-30 15:01:51 -08002547 * You should specify the IRQs too in that case "irq=....,...".
2548 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07002549 * More than 4 boards in one computer is not possible, as the card can
Jeff Garzikd61780c2005-10-30 15:01:51 -08002550 * only use 4 different interrupts.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002551 *
2552 */
2553static int __init specialix_init_module(void)
2554{
2555 int i;
2556
2557 func_enter();
2558
2559 init_MUTEX(&tmp_buf_sem); /* Init de the semaphore - pvdl */
2560
2561 if (iobase[0] || iobase[1] || iobase[2] || iobase[3]) {
2562 for(i = 0; i < SX_NBOARD; i++) {
2563 sx_board[i].base = iobase[i];
2564 sx_board[i].irq = irq[i];
2565 sx_board[i].count= 0;
2566 }
2567 }
2568
2569 func_exit();
2570
2571 return specialix_init();
2572}
Jeff Garzikd61780c2005-10-30 15:01:51 -08002573
Linus Torvalds1da177e2005-04-16 15:20:36 -07002574static void __exit specialix_exit_module(void)
2575{
2576 int i;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002577
Linus Torvalds1da177e2005-04-16 15:20:36 -07002578 func_enter();
2579
2580 sx_release_drivers();
2581 for (i = 0; i < SX_NBOARD; i++)
Jeff Garzikd61780c2005-10-30 15:01:51 -08002582 if (sx_board[i].flags & SX_BOARD_PRESENT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002583 sx_release_io_range(&sx_board[i]);
2584#ifdef SPECIALIX_TIMER
2585 del_timer (&missed_irq_timer);
2586#endif
2587
2588 func_exit();
2589}
2590
2591module_init(specialix_init_module);
2592module_exit(specialix_exit_module);
2593
2594MODULE_LICENSE("GPL");