blob: 352547eabf7b171bb4a676d7d772f65a1e575a15 [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>
88#include <linux/mm.h>
89#include <linux/serial.h>
90#include <linux/fcntl.h>
91#include <linux/major.h>
92#include <linux/delay.h>
93#include <linux/version.h>
94#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;
669 unsigned char ch;
670
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;
680 dprintk (SX_DEBUG_RX, "port: %p count: %d BUFF_SIZE: %d\n",
681 port, tty->flip.count, TTY_FLIPBUF_SIZE);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800682
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 status = sx_in(bp, CD186x_RCSR);
684
685 dprintk (SX_DEBUG_RX, "status: 0x%x\n", status);
686 if (status & RCSR_OE) {
687 port->overrun++;
688 dprintk(SX_DEBUG_FIFO, "sx%d: port %d: Overrun. Total %ld overruns.\n",
689 board_No(bp), port_No(port), port->overrun);
690 }
691 status &= port->mark_mask;
692
693 /* This flip buffer check needs to be below the reading of the
694 status register to reset the chip's IRQ.... */
695 if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
696 dprintk(SX_DEBUG_FIFO, "sx%d: port %d: Working around flip buffer overflow.\n",
697 board_No(bp), port_No(port));
698 func_exit();
699 return;
700 }
701
702 ch = sx_in(bp, CD186x_RDR);
703 if (!status) {
704 func_exit();
705 return;
706 }
707 if (status & RCSR_TOUT) {
Jeff Garzikd61780c2005-10-30 15:01:51 -0800708 printk(KERN_INFO "sx%d: port %d: Receiver timeout. Hardware problems ?\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709 board_No(bp), port_No(port));
710 func_exit();
711 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800712
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713 } else if (status & RCSR_BREAK) {
714 dprintk(SX_DEBUG_RX, "sx%d: port %d: Handling break...\n",
715 board_No(bp), port_No(port));
716 *tty->flip.flag_buf_ptr++ = TTY_BREAK;
717 if (port->flags & ASYNC_SAK)
718 do_SAK(tty);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800719
720 } else if (status & RCSR_PE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721 *tty->flip.flag_buf_ptr++ = TTY_PARITY;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800722
723 else if (status & RCSR_FE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 *tty->flip.flag_buf_ptr++ = TTY_FRAME;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800725
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726 else if (status & RCSR_OE)
727 *tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800728
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729 else
730 *tty->flip.flag_buf_ptr++ = 0;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800731
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732 *tty->flip.char_buf_ptr++ = ch;
733 tty->flip.count++;
734 schedule_delayed_work(&tty->flip.work, 1);
735
736 func_exit();
737}
738
739
740static inline void sx_receive(struct specialix_board * bp)
741{
742 struct specialix_port *port;
743 struct tty_struct *tty;
744 unsigned char count;
745
746 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -0800747
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748 if (!(port = sx_get_port(bp, "Receive"))) {
749 dprintk (SX_DEBUG_RX, "Hmm, couldn't find port.\n");
750 func_exit();
751 return;
752 }
753 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800754
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755 count = sx_in(bp, CD186x_RDCR);
756 dprintk (SX_DEBUG_RX, "port: %p: count: %d\n", port, count);
757 port->hits[count > 8 ? 9 : count]++;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800758
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759 while (count--) {
760 if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
761 printk(KERN_INFO "sx%d: port %d: Working around flip buffer overflow.\n",
762 board_No(bp), port_No(port));
763 break;
764 }
765 *tty->flip.char_buf_ptr++ = sx_in(bp, CD186x_RDR);
766 *tty->flip.flag_buf_ptr++ = 0;
767 tty->flip.count++;
768 }
769 schedule_delayed_work(&tty->flip.work, 1);
770
771 func_exit();
772}
773
774
775static inline void sx_transmit(struct specialix_board * bp)
776{
777 struct specialix_port *port;
778 struct tty_struct *tty;
779 unsigned char count;
780
781 func_enter();
782 if (!(port = sx_get_port(bp, "Transmit"))) {
783 func_exit();
784 return;
785 }
786 dprintk (SX_DEBUG_TX, "port: %p\n", port);
787 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800788
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789 if (port->IER & IER_TXEMPTY) {
790 /* FIFO drained */
791 sx_out(bp, CD186x_CAR, port_No(port));
792 port->IER &= ~IER_TXEMPTY;
793 sx_out(bp, CD186x_IER, port->IER);
794 func_exit();
795 return;
796 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800797
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 if ((port->xmit_cnt <= 0 && !port->break_length)
799 || tty->stopped || tty->hw_stopped) {
800 sx_out(bp, CD186x_CAR, port_No(port));
801 port->IER &= ~IER_TXRDY;
802 sx_out(bp, CD186x_IER, port->IER);
803 func_exit();
804 return;
805 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800806
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807 if (port->break_length) {
808 if (port->break_length > 0) {
809 if (port->COR2 & COR2_ETC) {
810 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
811 sx_out(bp, CD186x_TDR, CD186x_C_SBRK);
812 port->COR2 &= ~COR2_ETC;
813 }
814 count = min_t(int, port->break_length, 0xff);
815 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
816 sx_out(bp, CD186x_TDR, CD186x_C_DELAY);
817 sx_out(bp, CD186x_TDR, count);
818 if (!(port->break_length -= count))
819 port->break_length--;
820 } else {
821 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
822 sx_out(bp, CD186x_TDR, CD186x_C_EBRK);
823 sx_out(bp, CD186x_COR2, port->COR2);
824 sx_wait_CCR(bp);
825 sx_out(bp, CD186x_CCR, CCR_CORCHG2);
826 port->break_length = 0;
827 }
828
829 func_exit();
830 return;
831 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800832
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833 count = CD186x_NFIFO;
834 do {
835 sx_out(bp, CD186x_TDR, port->xmit_buf[port->xmit_tail++]);
836 port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1);
837 if (--port->xmit_cnt <= 0)
838 break;
839 } while (--count > 0);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800840
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841 if (port->xmit_cnt <= 0) {
842 sx_out(bp, CD186x_CAR, port_No(port));
843 port->IER &= ~IER_TXRDY;
844 sx_out(bp, CD186x_IER, port->IER);
845 }
846 if (port->xmit_cnt <= port->wakeup_chars)
847 sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);
848
849 func_exit();
850}
851
852
853static inline void sx_check_modem(struct specialix_board * bp)
854{
855 struct specialix_port *port;
856 struct tty_struct *tty;
857 unsigned char mcr;
858 int msvr_cd;
859
860 dprintk (SX_DEBUG_SIGNALS, "Modem intr. ");
861 if (!(port = sx_get_port(bp, "Modem")))
862 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800863
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800865
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866 mcr = sx_in(bp, CD186x_MCR);
867 printk ("mcr = %02x.\n", mcr);
868
869 if ((mcr & MCR_CDCHG)) {
870 dprintk (SX_DEBUG_SIGNALS, "CD just changed... ");
871 msvr_cd = sx_in(bp, CD186x_MSVR) & MSVR_CD;
872 if (msvr_cd) {
873 dprintk (SX_DEBUG_SIGNALS, "Waking up guys in open.\n");
874 wake_up_interruptible(&port->open_wait);
875 } else {
876 dprintk (SX_DEBUG_SIGNALS, "Sending HUP.\n");
877 schedule_work(&port->tqueue_hangup);
878 }
879 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800880
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
882 if (mcr & MCR_CTSCHG) {
883 if (sx_in(bp, CD186x_MSVR) & MSVR_CTS) {
884 tty->hw_stopped = 0;
885 port->IER |= IER_TXRDY;
886 if (port->xmit_cnt <= port->wakeup_chars)
887 sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);
888 } else {
889 tty->hw_stopped = 1;
890 port->IER &= ~IER_TXRDY;
891 }
892 sx_out(bp, CD186x_IER, port->IER);
893 }
894 if (mcr & MCR_DSSXHG) {
895 if (sx_in(bp, CD186x_MSVR) & MSVR_DSR) {
896 tty->hw_stopped = 0;
897 port->IER |= IER_TXRDY;
898 if (port->xmit_cnt <= port->wakeup_chars)
899 sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);
900 } else {
901 tty->hw_stopped = 1;
902 port->IER &= ~IER_TXRDY;
903 }
904 sx_out(bp, CD186x_IER, port->IER);
905 }
906#endif /* SPECIALIX_BRAIN_DAMAGED_CTS */
Jeff Garzikd61780c2005-10-30 15:01:51 -0800907
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908 /* Clear change bits */
909 sx_out(bp, CD186x_MCR, 0);
910}
911
912
913/* The main interrupt processing routine */
914static irqreturn_t sx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
915{
916 unsigned char status;
917 unsigned char ack;
918 struct specialix_board *bp;
919 unsigned long loop = 0;
920 int saved_reg;
921 unsigned long flags;
922
923 func_enter();
924
925 bp = dev_id;
926 spin_lock_irqsave(&bp->lock, flags);
927
928 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);
929 if (!bp || !(bp->flags & SX_BOARD_ACTIVE)) {
930 dprintk (SX_DEBUG_IRQ, "sx: False interrupt. irq %d.\n", irq);
931 spin_unlock_irqrestore(&bp->lock, flags);
932 func_exit();
933 return IRQ_NONE;
934 }
935
936 saved_reg = bp->reg;
937
938 while ((++loop < 16) && (status = (sx_in(bp, CD186x_SRSR) &
939 (SRSR_RREQint |
940 SRSR_TREQint |
Jeff Garzikd61780c2005-10-30 15:01:51 -0800941 SRSR_MREQint)))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942 if (status & SRSR_RREQint) {
943 ack = sx_in(bp, CD186x_RRAR);
944
945 if (ack == (SX_ID | GIVR_IT_RCV))
946 sx_receive(bp);
947 else if (ack == (SX_ID | GIVR_IT_REXC))
948 sx_receive_exc(bp);
949 else
950 printk(KERN_ERR "sx%d: status: 0x%x Bad receive ack 0x%02x.\n",
951 board_No(bp), status, ack);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800952
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953 } else if (status & SRSR_TREQint) {
954 ack = sx_in(bp, CD186x_TRAR);
955
956 if (ack == (SX_ID | GIVR_IT_TX))
957 sx_transmit(bp);
958 else
959 printk(KERN_ERR "sx%d: status: 0x%x Bad transmit ack 0x%02x. port: %d\n",
960 board_No(bp), status, ack, port_No (sx_get_port (bp, "Int")));
961 } else if (status & SRSR_MREQint) {
962 ack = sx_in(bp, CD186x_MRAR);
963
Jeff Garzikd61780c2005-10-30 15:01:51 -0800964 if (ack == (SX_ID | GIVR_IT_MODEM))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965 sx_check_modem(bp);
966 else
967 printk(KERN_ERR "sx%d: status: 0x%x Bad modem ack 0x%02x.\n",
968 board_No(bp), status, ack);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800969
970 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971
972 sx_out(bp, CD186x_EOIR, 0); /* Mark end of interrupt */
973 }
974 bp->reg = saved_reg;
975 outb (bp->reg, bp->base + SX_ADDR_REG);
976 spin_unlock_irqrestore(&bp->lock, flags);
977 func_exit();
978 return IRQ_HANDLED;
979}
980
981
982/*
983 * Routines for open & close processing.
984 */
985
986static void turn_ints_off (struct specialix_board *bp)
987{
988 unsigned long flags;
989
990 func_enter();
991 if (bp->flags & SX_BOARD_IS_PCI) {
992 /* This was intended for enabeling the interrupt on the
993 * PCI card. However it seems that it's already enabled
994 * and as PCI interrupts can be shared, there is no real
995 * reason to have to turn it off. */
996 }
997
998 spin_lock_irqsave(&bp->lock, flags);
999 (void) sx_in_off (bp, 0); /* Turn off interrupts. */
1000 spin_unlock_irqrestore(&bp->lock, flags);
1001
1002 func_exit();
1003}
1004
1005static void turn_ints_on (struct specialix_board *bp)
1006{
1007 unsigned long flags;
1008
1009 func_enter();
1010
1011 if (bp->flags & SX_BOARD_IS_PCI) {
1012 /* play with the PCI chip. See comment above. */
1013 }
1014 spin_lock_irqsave(&bp->lock, flags);
1015 (void) sx_in (bp, 0); /* Turn ON interrupts. */
1016 spin_unlock_irqrestore(&bp->lock, flags);
1017
1018 func_exit();
1019}
1020
1021
1022/* Called with disabled interrupts */
1023static inline int sx_setup_board(struct specialix_board * bp)
1024{
1025 int error;
1026
Jeff Garzikd61780c2005-10-30 15:01:51 -08001027 if (bp->flags & SX_BOARD_ACTIVE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028 return 0;
1029
1030 if (bp->flags & SX_BOARD_IS_PCI)
1031 error = request_irq(bp->irq, sx_interrupt, SA_INTERRUPT | SA_SHIRQ, "specialix IO8+", bp);
1032 else
1033 error = request_irq(bp->irq, sx_interrupt, SA_INTERRUPT, "specialix IO8+", bp);
1034
Jeff Garzikd61780c2005-10-30 15:01:51 -08001035 if (error)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036 return error;
1037
1038 turn_ints_on (bp);
1039 bp->flags |= SX_BOARD_ACTIVE;
1040
1041 return 0;
1042}
1043
1044
1045/* Called with disabled interrupts */
1046static inline void sx_shutdown_board(struct specialix_board *bp)
1047{
1048 func_enter();
1049
1050 if (!(bp->flags & SX_BOARD_ACTIVE)) {
1051 func_exit();
1052 return;
1053 }
1054
1055 bp->flags &= ~SX_BOARD_ACTIVE;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001056
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057 dprintk (SX_DEBUG_IRQ, "Freeing IRQ%d for board %d.\n",
1058 bp->irq, board_No (bp));
1059 free_irq(bp->irq, bp);
1060
1061 turn_ints_off (bp);
1062
1063
1064 func_exit();
1065}
1066
1067
1068/*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001069 * Setting up port characteristics.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 * Must be called with disabled interrupts
1071 */
1072static void sx_change_speed(struct specialix_board *bp, struct specialix_port *port)
1073{
1074 struct tty_struct *tty;
1075 unsigned long baud;
1076 long tmp;
1077 unsigned char cor1 = 0, cor3 = 0;
1078 unsigned char mcor1 = 0, mcor2 = 0;
1079 static unsigned long again;
1080 unsigned long flags;
1081
1082 func_enter();
1083
1084 if (!(tty = port->tty) || !tty->termios) {
1085 func_exit();
1086 return;
1087 }
1088
1089 port->IER = 0;
1090 port->COR2 = 0;
1091 /* Select port on the board */
1092 spin_lock_irqsave(&bp->lock, flags);
1093 sx_out(bp, CD186x_CAR, port_No(port));
1094
1095 /* The Specialix board doens't implement the RTS lines.
1096 They are used to set the IRQ level. Don't touch them. */
1097 if (SX_CRTSCTS(tty))
1098 port->MSVR = MSVR_DTR | (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
1099 else
1100 port->MSVR = (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
1101 spin_unlock_irqrestore(&bp->lock, flags);
1102 dprintk (SX_DEBUG_TERMIOS, "sx: got MSVR=%02x.\n", port->MSVR);
1103 baud = C_BAUD(tty);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001104
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105 if (baud & CBAUDEX) {
1106 baud &= ~CBAUDEX;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001107 if (baud < 1 || baud > 2)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108 port->tty->termios->c_cflag &= ~CBAUDEX;
1109 else
1110 baud += 15;
1111 }
1112 if (baud == 15) {
1113 if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
1114 baud ++;
1115 if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
1116 baud += 2;
1117 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001118
1119
Linus Torvalds1da177e2005-04-16 15:20:36 -07001120 if (!baud_table[baud]) {
1121 /* Drop DTR & exit */
1122 dprintk (SX_DEBUG_TERMIOS, "Dropping DTR... Hmm....\n");
1123 if (!SX_CRTSCTS (tty)) {
1124 port -> MSVR &= ~ MSVR_DTR;
1125 spin_lock_irqsave(&bp->lock, flags);
1126 sx_out(bp, CD186x_MSVR, port->MSVR );
1127 spin_unlock_irqrestore(&bp->lock, flags);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001128 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129 else
1130 dprintk (SX_DEBUG_TERMIOS, "Can't drop DTR: no DTR.\n");
1131 return;
1132 } else {
1133 /* Set DTR on */
1134 if (!SX_CRTSCTS (tty)) {
1135 port ->MSVR |= MSVR_DTR;
1136 }
1137 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001138
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139 /*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001140 * Now we must calculate some speed depended things
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141 */
1142
1143 /* Set baud rate for port */
1144 tmp = port->custom_divisor ;
1145 if ( tmp )
1146 printk (KERN_INFO "sx%d: Using custom baud rate divisor %ld. \n"
1147 "This is an untested option, please be carefull.\n",
1148 port_No (port), tmp);
1149 else
1150 tmp = (((SX_OSCFREQ + baud_table[baud]/2) / baud_table[baud] +
1151 CD186x_TPC/2) / CD186x_TPC);
1152
Jeff Garzikd61780c2005-10-30 15:01:51 -08001153 if ((tmp < 0x10) && time_before(again, jiffies)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 again = jiffies + HZ * 60;
1155 /* Page 48 of version 2.0 of the CL-CD1865 databook */
1156 if (tmp >= 12) {
1157 printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
1158 "Performance degradation is possible.\n"
1159 "Read specialix.txt for more info.\n",
1160 port_No (port), tmp);
1161 } else {
1162 printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
1163 "Warning: overstressing Cirrus chip. "
1164 "This might not work.\n"
Jeff Garzikd61780c2005-10-30 15:01:51 -08001165 "Read specialix.txt for more info.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166 port_No (port), tmp);
1167 }
1168 }
1169 spin_lock_irqsave(&bp->lock, flags);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001170 sx_out(bp, CD186x_RBPRH, (tmp >> 8) & 0xff);
1171 sx_out(bp, CD186x_TBPRH, (tmp >> 8) & 0xff);
1172 sx_out(bp, CD186x_RBPRL, tmp & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001173 sx_out(bp, CD186x_TBPRL, tmp & 0xff);
1174 spin_unlock_irqrestore(&bp->lock, flags);
1175 if (port->custom_divisor) {
1176 baud = (SX_OSCFREQ + port->custom_divisor/2) / port->custom_divisor;
1177 baud = ( baud + 5 ) / 10;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001178 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001179 baud = (baud_table[baud] + 5) / 10; /* Estimated CPS */
1180
1181 /* Two timer ticks seems enough to wakeup something like SLIP driver */
Jeff Garzikd61780c2005-10-30 15:01:51 -08001182 tmp = ((baud + HZ/2) / HZ) * 2 - CD186x_NFIFO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001183 port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ?
1184 SERIAL_XMIT_SIZE - 1 : tmp);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001185
Linus Torvalds1da177e2005-04-16 15:20:36 -07001186 /* Receiver timeout will be transmission time for 1.5 chars */
1187 tmp = (SPECIALIX_TPS + SPECIALIX_TPS/2 + baud/2) / baud;
1188 tmp = (tmp > 0xff) ? 0xff : tmp;
1189 spin_lock_irqsave(&bp->lock, flags);
1190 sx_out(bp, CD186x_RTPR, tmp);
1191 spin_unlock_irqrestore(&bp->lock, flags);
1192 switch (C_CSIZE(tty)) {
1193 case CS5:
1194 cor1 |= COR1_5BITS;
1195 break;
1196 case CS6:
1197 cor1 |= COR1_6BITS;
1198 break;
1199 case CS7:
1200 cor1 |= COR1_7BITS;
1201 break;
1202 case CS8:
1203 cor1 |= COR1_8BITS;
1204 break;
1205 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001206
1207 if (C_CSTOPB(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208 cor1 |= COR1_2SB;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001209
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210 cor1 |= COR1_IGNORE;
1211 if (C_PARENB(tty)) {
1212 cor1 |= COR1_NORMPAR;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001213 if (C_PARODD(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214 cor1 |= COR1_ODDP;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001215 if (I_INPCK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001216 cor1 &= ~COR1_IGNORE;
1217 }
1218 /* Set marking of some errors */
1219 port->mark_mask = RCSR_OE | RCSR_TOUT;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001220 if (I_INPCK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001221 port->mark_mask |= RCSR_FE | RCSR_PE;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001222 if (I_BRKINT(tty) || I_PARMRK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001223 port->mark_mask |= RCSR_BREAK;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001224 if (I_IGNPAR(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225 port->mark_mask &= ~(RCSR_FE | RCSR_PE);
1226 if (I_IGNBRK(tty)) {
1227 port->mark_mask &= ~RCSR_BREAK;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001228 if (I_IGNPAR(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001229 /* Real raw mode. Ignore all */
1230 port->mark_mask &= ~RCSR_OE;
1231 }
1232 /* Enable Hardware Flow Control */
1233 if (C_CRTSCTS(tty)) {
1234#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
1235 port->IER |= IER_DSR | IER_CTS;
1236 mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD;
1237 mcor2 |= MCOR2_DSROD | MCOR2_CTSOD;
1238 spin_lock_irqsave(&bp->lock, flags);
1239 tty->hw_stopped = !(sx_in(bp, CD186x_MSVR) & (MSVR_CTS|MSVR_DSR));
1240 spin_unlock_irqrestore(&bp->lock, flags);
1241#else
Jeff Garzikd61780c2005-10-30 15:01:51 -08001242 port->COR2 |= COR2_CTSAE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001243#endif
1244 }
1245 /* Enable Software Flow Control. FIXME: I'm not sure about this */
1246 /* Some people reported that it works, but I still doubt it */
1247 if (I_IXON(tty)) {
1248 port->COR2 |= COR2_TXIBE;
1249 cor3 |= (COR3_FCT | COR3_SCDE);
1250 if (I_IXANY(tty))
1251 port->COR2 |= COR2_IXM;
1252 spin_lock_irqsave(&bp->lock, flags);
1253 sx_out(bp, CD186x_SCHR1, START_CHAR(tty));
1254 sx_out(bp, CD186x_SCHR2, STOP_CHAR(tty));
1255 sx_out(bp, CD186x_SCHR3, START_CHAR(tty));
1256 sx_out(bp, CD186x_SCHR4, STOP_CHAR(tty));
1257 spin_unlock_irqrestore(&bp->lock, flags);
1258 }
1259 if (!C_CLOCAL(tty)) {
1260 /* Enable CD check */
1261 port->IER |= IER_CD;
1262 mcor1 |= MCOR1_CDZD;
1263 mcor2 |= MCOR2_CDOD;
1264 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001265
1266 if (C_CREAD(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001267 /* Enable receiver */
1268 port->IER |= IER_RXD;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001269
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270 /* Set input FIFO size (1-8 bytes) */
1271 cor3 |= sx_rxfifo;
1272 /* Setting up CD186x channel registers */
1273 spin_lock_irqsave(&bp->lock, flags);
1274 sx_out(bp, CD186x_COR1, cor1);
1275 sx_out(bp, CD186x_COR2, port->COR2);
1276 sx_out(bp, CD186x_COR3, cor3);
1277 spin_unlock_irqrestore(&bp->lock, flags);
1278 /* Make CD186x know about registers change */
1279 sx_wait_CCR(bp);
1280 spin_lock_irqsave(&bp->lock, flags);
1281 sx_out(bp, CD186x_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3);
1282 /* Setting up modem option registers */
1283 dprintk (SX_DEBUG_TERMIOS, "Mcor1 = %02x, mcor2 = %02x.\n", mcor1, mcor2);
1284 sx_out(bp, CD186x_MCOR1, mcor1);
1285 sx_out(bp, CD186x_MCOR2, mcor2);
1286 spin_unlock_irqrestore(&bp->lock, flags);
1287 /* Enable CD186x transmitter & receiver */
1288 sx_wait_CCR(bp);
1289 spin_lock_irqsave(&bp->lock, flags);
1290 sx_out(bp, CD186x_CCR, CCR_TXEN | CCR_RXEN);
1291 /* Enable interrupts */
1292 sx_out(bp, CD186x_IER, port->IER);
1293 /* And finally set the modem lines... */
1294 sx_out(bp, CD186x_MSVR, port->MSVR);
1295 spin_unlock_irqrestore(&bp->lock, flags);
1296
1297 func_exit();
1298}
1299
1300
1301/* Must be called with interrupts enabled */
1302static int sx_setup_port(struct specialix_board *bp, struct specialix_port *port)
1303{
1304 unsigned long flags;
1305
1306 func_enter();
1307
1308 if (port->flags & ASYNC_INITIALIZED) {
1309 func_exit();
1310 return 0;
1311 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001312
Linus Torvalds1da177e2005-04-16 15:20:36 -07001313 if (!port->xmit_buf) {
1314 /* We may sleep in get_zeroed_page() */
1315 unsigned long tmp;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001316
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317 if (!(tmp = get_zeroed_page(GFP_KERNEL))) {
1318 func_exit();
1319 return -ENOMEM;
1320 }
1321
1322 if (port->xmit_buf) {
1323 free_page(tmp);
1324 func_exit();
1325 return -ERESTARTSYS;
1326 }
1327 port->xmit_buf = (unsigned char *) tmp;
1328 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001329
Linus Torvalds1da177e2005-04-16 15:20:36 -07001330 spin_lock_irqsave(&port->lock, flags);
1331
Jeff Garzikd61780c2005-10-30 15:01:51 -08001332 if (port->tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001333 clear_bit(TTY_IO_ERROR, &port->tty->flags);
1334
1335 port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
1336 sx_change_speed(bp, port);
1337 port->flags |= ASYNC_INITIALIZED;
1338
1339 spin_unlock_irqrestore(&port->lock, flags);
1340
Jeff Garzikd61780c2005-10-30 15:01:51 -08001341
Linus Torvalds1da177e2005-04-16 15:20:36 -07001342 func_exit();
1343 return 0;
1344}
1345
1346
1347/* Must be called with interrupts disabled */
1348static void sx_shutdown_port(struct specialix_board *bp, struct specialix_port *port)
1349{
1350 struct tty_struct *tty;
1351 int i;
1352 unsigned long flags;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001353
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354 func_enter();
1355
1356 if (!(port->flags & ASYNC_INITIALIZED)) {
1357 func_exit();
1358 return;
1359 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001360
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361 if (sx_debug & SX_DEBUG_FIFO) {
1362 dprintk(SX_DEBUG_FIFO, "sx%d: port %d: %ld overruns, FIFO hits [ ",
1363 board_No(bp), port_No(port), port->overrun);
1364 for (i = 0; i < 10; i++) {
1365 dprintk(SX_DEBUG_FIFO, "%ld ", port->hits[i]);
1366 }
1367 dprintk(SX_DEBUG_FIFO, "].\n");
1368 }
1369
1370 if (port->xmit_buf) {
1371 free_page((unsigned long) port->xmit_buf);
1372 port->xmit_buf = NULL;
1373 }
1374
1375 /* Select port */
1376 spin_lock_irqsave(&bp->lock, flags);
1377 sx_out(bp, CD186x_CAR, port_No(port));
1378
1379 if (!(tty = port->tty) || C_HUPCL(tty)) {
1380 /* Drop DTR */
1381 sx_out(bp, CD186x_MSVDTR, 0);
1382 }
1383 spin_unlock_irqrestore(&bp->lock, flags);
1384 /* Reset port */
1385 sx_wait_CCR(bp);
1386 spin_lock_irqsave(&bp->lock, flags);
1387 sx_out(bp, CD186x_CCR, CCR_SOFTRESET);
1388 /* Disable all interrupts from this port */
1389 port->IER = 0;
1390 sx_out(bp, CD186x_IER, port->IER);
1391 spin_unlock_irqrestore(&bp->lock, flags);
1392 if (tty)
1393 set_bit(TTY_IO_ERROR, &tty->flags);
1394 port->flags &= ~ASYNC_INITIALIZED;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001395
1396 if (!bp->count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001397 sx_shutdown_board(bp);
1398 func_exit();
1399}
1400
Jeff Garzikd61780c2005-10-30 15:01:51 -08001401
Linus Torvalds1da177e2005-04-16 15:20:36 -07001402static int block_til_ready(struct tty_struct *tty, struct file * filp,
1403 struct specialix_port *port)
1404{
1405 DECLARE_WAITQUEUE(wait, current);
1406 struct specialix_board *bp = port_Board(port);
1407 int retval;
1408 int do_clocal = 0;
1409 int CD;
1410 unsigned long flags;
1411
1412 func_enter();
1413
1414 /*
1415 * If the device is in the middle of being closed, then block
1416 * until it's done, and then try again.
1417 */
1418 if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {
1419 interruptible_sleep_on(&port->close_wait);
1420 if (port->flags & ASYNC_HUP_NOTIFY) {
1421 func_exit();
1422 return -EAGAIN;
1423 } else {
1424 func_exit();
1425 return -ERESTARTSYS;
1426 }
1427 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001428
Linus Torvalds1da177e2005-04-16 15:20:36 -07001429 /*
1430 * If non-blocking mode is set, or the port is not enabled,
1431 * then make the check up front and then exit.
1432 */
1433 if ((filp->f_flags & O_NONBLOCK) ||
1434 (tty->flags & (1 << TTY_IO_ERROR))) {
1435 port->flags |= ASYNC_NORMAL_ACTIVE;
1436 func_exit();
1437 return 0;
1438 }
1439
1440 if (C_CLOCAL(tty))
1441 do_clocal = 1;
1442
1443 /*
1444 * Block waiting for the carrier detect and the line to become
1445 * free (i.e., not in use by the callout). While we are in
1446 * this loop, info->count is dropped by one, so that
1447 * rs_close() knows when to free things. We restore it upon
1448 * exit, either normal or abnormal.
1449 */
1450 retval = 0;
1451 add_wait_queue(&port->open_wait, &wait);
1452 spin_lock_irqsave(&port->lock, flags);
1453 if (!tty_hung_up_p(filp)) {
1454 port->count--;
1455 }
1456 spin_unlock_irqrestore(&port->lock, flags);
1457 port->blocked_open++;
1458 while (1) {
1459 spin_lock_irqsave(&bp->lock, flags);
1460 sx_out(bp, CD186x_CAR, port_No(port));
1461 CD = sx_in(bp, CD186x_MSVR) & MSVR_CD;
1462 if (SX_CRTSCTS (tty)) {
1463 /* Activate RTS */
1464 port->MSVR |= MSVR_DTR; /* WTF? */
1465 sx_out (bp, CD186x_MSVR, port->MSVR);
1466 } else {
1467 /* Activate DTR */
1468 port->MSVR |= MSVR_DTR;
1469 sx_out (bp, CD186x_MSVR, port->MSVR);
1470 }
1471 spin_unlock_irqrestore(&bp->lock, flags);
1472 set_current_state(TASK_INTERRUPTIBLE);
1473 if (tty_hung_up_p(filp) ||
1474 !(port->flags & ASYNC_INITIALIZED)) {
1475 if (port->flags & ASYNC_HUP_NOTIFY)
1476 retval = -EAGAIN;
1477 else
Jeff Garzikd61780c2005-10-30 15:01:51 -08001478 retval = -ERESTARTSYS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001479 break;
1480 }
1481 if (!(port->flags & ASYNC_CLOSING) &&
1482 (do_clocal || CD))
1483 break;
1484 if (signal_pending(current)) {
1485 retval = -ERESTARTSYS;
1486 break;
1487 }
1488 schedule();
1489 }
1490
1491 set_current_state(TASK_RUNNING);
1492 remove_wait_queue(&port->open_wait, &wait);
1493 spin_lock_irqsave(&port->lock, flags);
1494 if (!tty_hung_up_p(filp)) {
1495 port->count++;
1496 }
1497 port->blocked_open--;
1498 spin_unlock_irqrestore(&port->lock, flags);
1499 if (retval) {
1500 func_exit();
1501 return retval;
1502 }
1503
1504 port->flags |= ASYNC_NORMAL_ACTIVE;
1505 func_exit();
1506 return 0;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001507}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001508
1509
1510static int sx_open(struct tty_struct * tty, struct file * filp)
1511{
1512 int board;
1513 int error;
1514 struct specialix_port * port;
1515 struct specialix_board * bp;
1516 int i;
1517 unsigned long flags;
1518
1519 func_enter();
1520
1521 board = SX_BOARD(tty->index);
1522
1523 if (board >= SX_NBOARD || !(sx_board[board].flags & SX_BOARD_PRESENT)) {
1524 func_exit();
1525 return -ENODEV;
1526 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001527
Linus Torvalds1da177e2005-04-16 15:20:36 -07001528 bp = &sx_board[board];
1529 port = sx_port + board * SX_NPORT + SX_PORT(tty->index);
1530 port->overrun = 0;
1531 for (i = 0; i < 10; i++)
1532 port->hits[i]=0;
1533
1534 dprintk (SX_DEBUG_OPEN, "Board = %d, bp = %p, port = %p, portno = %d.\n",
1535 board, bp, port, SX_PORT(tty->index));
1536
1537 if (sx_paranoia_check(port, tty->name, "sx_open")) {
1538 func_enter();
1539 return -ENODEV;
1540 }
1541
1542 if ((error = sx_setup_board(bp))) {
1543 func_exit();
1544 return error;
1545 }
1546
1547 spin_lock_irqsave(&bp->lock, flags);
1548 port->count++;
1549 bp->count++;
1550 tty->driver_data = port;
1551 port->tty = tty;
1552 spin_unlock_irqrestore(&bp->lock, flags);
1553
1554 if ((error = sx_setup_port(bp, port))) {
1555 func_enter();
1556 return error;
1557 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001558
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559 if ((error = block_til_ready(tty, filp, port))) {
1560 func_enter();
1561 return error;
1562 }
1563
1564 func_exit();
1565 return 0;
1566}
1567
1568
1569static void sx_close(struct tty_struct * tty, struct file * filp)
1570{
1571 struct specialix_port *port = (struct specialix_port *) tty->driver_data;
1572 struct specialix_board *bp;
1573 unsigned long flags;
1574 unsigned long timeout;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001575
Linus Torvalds1da177e2005-04-16 15:20:36 -07001576 func_enter();
1577 if (!port || sx_paranoia_check(port, tty->name, "close")) {
1578 func_exit();
1579 return;
1580 }
1581 spin_lock_irqsave(&port->lock, flags);
1582
1583 if (tty_hung_up_p(filp)) {
1584 spin_unlock_irqrestore(&port->lock, flags);
1585 func_exit();
1586 return;
1587 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001588
Linus Torvalds1da177e2005-04-16 15:20:36 -07001589 bp = port_Board(port);
1590 if ((tty->count == 1) && (port->count != 1)) {
1591 printk(KERN_ERR "sx%d: sx_close: bad port count;"
1592 " tty->count is 1, port count is %d\n",
1593 board_No(bp), port->count);
1594 port->count = 1;
1595 }
1596
1597 if (port->count > 1) {
1598 port->count--;
1599 bp->count--;
1600
1601 spin_unlock_irqrestore(&port->lock, flags);
1602
1603 func_exit();
1604 return;
1605 }
1606 port->flags |= ASYNC_CLOSING;
1607 /*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001608 * Now we wait for the transmit buffer to clear; and we notify
Linus Torvalds1da177e2005-04-16 15:20:36 -07001609 * the line discipline to only process XON/XOFF characters.
1610 */
1611 tty->closing = 1;
1612 spin_unlock_irqrestore(&port->lock, flags);
1613 dprintk (SX_DEBUG_OPEN, "Closing\n");
1614 if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) {
1615 tty_wait_until_sent(tty, port->closing_wait);
1616 }
1617 /*
1618 * At this point we stop accepting input. To do this, we
1619 * disable the receive line status interrupts, and tell the
1620 * interrupt driver to stop checking the data ready bit in the
1621 * line status register.
1622 */
1623 dprintk (SX_DEBUG_OPEN, "Closed\n");
1624 port->IER &= ~IER_RXD;
1625 if (port->flags & ASYNC_INITIALIZED) {
1626 port->IER &= ~IER_TXRDY;
1627 port->IER |= IER_TXEMPTY;
1628 spin_lock_irqsave(&bp->lock, flags);
1629 sx_out(bp, CD186x_CAR, port_No(port));
1630 sx_out(bp, CD186x_IER, port->IER);
1631 spin_unlock_irqrestore(&bp->lock, flags);
1632 /*
1633 * Before we drop DTR, make sure the UART transmitter
1634 * has completely drained; this is especially
1635 * important if there is a transmit FIFO!
1636 */
1637 timeout = jiffies+HZ;
1638 while(port->IER & IER_TXEMPTY) {
1639 set_current_state (TASK_INTERRUPTIBLE);
1640 msleep_interruptible(jiffies_to_msecs(port->timeout));
1641 if (time_after(jiffies, timeout)) {
1642 printk (KERN_INFO "Timeout waiting for close\n");
1643 break;
1644 }
1645 }
1646
1647 }
1648
1649 if (--bp->count < 0) {
1650 printk(KERN_ERR "sx%d: sx_shutdown_port: bad board count: %d port: %d\n",
1651 board_No(bp), bp->count, tty->index);
1652 bp->count = 0;
1653 }
1654 if (--port->count < 0) {
1655 printk(KERN_ERR "sx%d: sx_close: bad port count for tty%d: %d\n",
1656 board_No(bp), port_No(port), port->count);
1657 port->count = 0;
1658 }
1659
1660 sx_shutdown_port(bp, port);
1661 if (tty->driver->flush_buffer)
1662 tty->driver->flush_buffer(tty);
1663 tty_ldisc_flush(tty);
1664 spin_lock_irqsave(&port->lock, flags);
1665 tty->closing = 0;
1666 port->event = 0;
1667 port->tty = NULL;
1668 spin_unlock_irqrestore(&port->lock, flags);
1669 if (port->blocked_open) {
1670 if (port->close_delay) {
1671 msleep_interruptible(jiffies_to_msecs(port->close_delay));
1672 }
1673 wake_up_interruptible(&port->open_wait);
1674 }
1675 port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
1676 wake_up_interruptible(&port->close_wait);
1677
1678 func_exit();
1679}
1680
1681
Jeff Garzikd61780c2005-10-30 15:01:51 -08001682static int sx_write(struct tty_struct * tty,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001683 const unsigned char *buf, int count)
1684{
1685 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1686 struct specialix_board *bp;
1687 int c, total = 0;
1688 unsigned long flags;
1689
1690 func_enter();
1691 if (sx_paranoia_check(port, tty->name, "sx_write")) {
1692 func_exit();
1693 return 0;
1694 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001695
Linus Torvalds1da177e2005-04-16 15:20:36 -07001696 bp = port_Board(port);
1697
1698 if (!tty || !port->xmit_buf || !tmp_buf) {
1699 func_exit();
1700 return 0;
1701 }
1702
1703 while (1) {
1704 spin_lock_irqsave(&port->lock, flags);
1705 c = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
1706 SERIAL_XMIT_SIZE - port->xmit_head));
1707 if (c <= 0) {
1708 spin_unlock_irqrestore(&port->lock, flags);
1709 break;
1710 }
1711 memcpy(port->xmit_buf + port->xmit_head, buf, c);
1712 port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
1713 port->xmit_cnt += c;
1714 spin_unlock_irqrestore(&port->lock, flags);
1715
1716 buf += c;
1717 count -= c;
1718 total += c;
1719 }
1720
1721 spin_lock_irqsave(&bp->lock, flags);
1722 if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
1723 !(port->IER & IER_TXRDY)) {
1724 port->IER |= IER_TXRDY;
1725 sx_out(bp, CD186x_CAR, port_No(port));
1726 sx_out(bp, CD186x_IER, port->IER);
1727 }
1728 spin_unlock_irqrestore(&bp->lock, flags);
1729 func_exit();
1730
1731 return total;
1732}
1733
1734
1735static void sx_put_char(struct tty_struct * tty, unsigned char ch)
1736{
1737 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1738 unsigned long flags;
1739 struct specialix_board * bp;
1740
1741 func_enter();
1742
1743 if (sx_paranoia_check(port, tty->name, "sx_put_char")) {
1744 func_exit();
1745 return;
1746 }
1747 dprintk (SX_DEBUG_TX, "check tty: %p %p\n", tty, port->xmit_buf);
1748 if (!tty || !port->xmit_buf) {
1749 func_exit();
1750 return;
1751 }
1752 bp = port_Board(port);
1753 spin_lock_irqsave(&port->lock, flags);
1754
1755 dprintk (SX_DEBUG_TX, "xmit_cnt: %d xmit_buf: %p\n", port->xmit_cnt, port->xmit_buf);
1756 if ((port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) || (!port->xmit_buf)) {
1757 spin_unlock_irqrestore(&port->lock, flags);
1758 dprintk (SX_DEBUG_TX, "Exit size\n");
1759 func_exit();
1760 return;
1761 }
1762 dprintk (SX_DEBUG_TX, "Handle xmit: %p %p\n", port, port->xmit_buf);
1763 port->xmit_buf[port->xmit_head++] = ch;
1764 port->xmit_head &= SERIAL_XMIT_SIZE - 1;
1765 port->xmit_cnt++;
1766 spin_unlock_irqrestore(&port->lock, flags);
1767
1768 func_exit();
1769}
1770
1771
1772static void sx_flush_chars(struct tty_struct * tty)
1773{
1774 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1775 unsigned long flags;
1776 struct specialix_board * bp = port_Board(port);
1777
1778 func_enter();
1779
1780 if (sx_paranoia_check(port, tty->name, "sx_flush_chars")) {
1781 func_exit();
1782 return;
1783 }
1784 if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
1785 !port->xmit_buf) {
1786 func_exit();
1787 return;
1788 }
1789 spin_lock_irqsave(&bp->lock, flags);
1790 port->IER |= IER_TXRDY;
1791 sx_out(port_Board(port), CD186x_CAR, port_No(port));
1792 sx_out(port_Board(port), CD186x_IER, port->IER);
1793 spin_unlock_irqrestore(&bp->lock, flags);
1794
1795 func_exit();
1796}
1797
1798
1799static int sx_write_room(struct tty_struct * tty)
1800{
1801 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1802 int ret;
1803
1804 func_enter();
1805
1806 if (sx_paranoia_check(port, tty->name, "sx_write_room")) {
1807 func_exit();
1808 return 0;
1809 }
1810
1811 ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;
1812 if (ret < 0)
1813 ret = 0;
1814
1815 func_exit();
1816 return ret;
1817}
1818
1819
1820static int sx_chars_in_buffer(struct tty_struct *tty)
1821{
1822 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1823
1824 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08001825
Linus Torvalds1da177e2005-04-16 15:20:36 -07001826 if (sx_paranoia_check(port, tty->name, "sx_chars_in_buffer")) {
1827 func_exit();
1828 return 0;
1829 }
1830 func_exit();
1831 return port->xmit_cnt;
1832}
1833
1834
1835static void sx_flush_buffer(struct tty_struct *tty)
1836{
1837 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1838 unsigned long flags;
1839 struct specialix_board * bp;
1840
1841 func_enter();
1842
1843 if (sx_paranoia_check(port, tty->name, "sx_flush_buffer")) {
1844 func_exit();
1845 return;
1846 }
1847
1848 bp = port_Board(port);
1849 spin_lock_irqsave(&port->lock, flags);
1850 port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
1851 spin_unlock_irqrestore(&port->lock, flags);
1852 tty_wakeup(tty);
1853
1854 func_exit();
1855}
1856
1857
1858static int sx_tiocmget(struct tty_struct *tty, struct file *file)
1859{
1860 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1861 struct specialix_board * bp;
1862 unsigned char status;
1863 unsigned int result;
1864 unsigned long flags;
1865
1866 func_enter();
1867
1868 if (sx_paranoia_check(port, tty->name, __FUNCTION__)) {
1869 func_exit();
1870 return -ENODEV;
1871 }
1872
1873 bp = port_Board(port);
1874 spin_lock_irqsave (&bp->lock, flags);
1875 sx_out(bp, CD186x_CAR, port_No(port));
1876 status = sx_in(bp, CD186x_MSVR);
1877 spin_unlock_irqrestore(&bp->lock, flags);
1878 dprintk (SX_DEBUG_INIT, "Got msvr[%d] = %02x, car = %d.\n",
1879 port_No(port), status, sx_in (bp, CD186x_CAR));
1880 dprintk (SX_DEBUG_INIT, "sx_port = %p, port = %p\n", sx_port, port);
1881 if (SX_CRTSCTS(port->tty)) {
Jeff Garzikd61780c2005-10-30 15:01:51 -08001882 result = /* (status & MSVR_RTS) ? */ TIOCM_DTR /* : 0) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001883 | ((status & MSVR_DTR) ? TIOCM_RTS : 0)
1884 | ((status & MSVR_CD) ? TIOCM_CAR : 0)
1885 |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */
1886 | ((status & MSVR_CTS) ? TIOCM_CTS : 0);
1887 } else {
Jeff Garzikd61780c2005-10-30 15:01:51 -08001888 result = /* (status & MSVR_RTS) ? */ TIOCM_RTS /* : 0) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001889 | ((status & MSVR_DTR) ? TIOCM_DTR : 0)
1890 | ((status & MSVR_CD) ? TIOCM_CAR : 0)
1891 |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */
1892 | ((status & MSVR_CTS) ? TIOCM_CTS : 0);
1893 }
1894
1895 func_exit();
1896
1897 return result;
1898}
1899
1900
1901static int sx_tiocmset(struct tty_struct *tty, struct file *file,
1902 unsigned int set, unsigned int clear)
1903{
1904 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1905 unsigned long flags;
1906 struct specialix_board *bp;
1907
1908 func_enter();
1909
1910 if (sx_paranoia_check(port, tty->name, __FUNCTION__)) {
1911 func_exit();
1912 return -ENODEV;
1913 }
1914
1915 bp = port_Board(port);
1916
1917 spin_lock_irqsave(&port->lock, flags);
1918 /* if (set & TIOCM_RTS)
1919 port->MSVR |= MSVR_RTS; */
1920 /* if (set & TIOCM_DTR)
1921 port->MSVR |= MSVR_DTR; */
1922
1923 if (SX_CRTSCTS(port->tty)) {
1924 if (set & TIOCM_RTS)
1925 port->MSVR |= MSVR_DTR;
1926 } else {
1927 if (set & TIOCM_DTR)
1928 port->MSVR |= MSVR_DTR;
1929 }
1930
1931 /* if (clear & TIOCM_RTS)
1932 port->MSVR &= ~MSVR_RTS; */
1933 /* if (clear & TIOCM_DTR)
1934 port->MSVR &= ~MSVR_DTR; */
1935 if (SX_CRTSCTS(port->tty)) {
1936 if (clear & TIOCM_RTS)
1937 port->MSVR &= ~MSVR_DTR;
1938 } else {
1939 if (clear & TIOCM_DTR)
1940 port->MSVR &= ~MSVR_DTR;
1941 }
1942 spin_lock_irqsave(&bp->lock, flags);
1943 sx_out(bp, CD186x_CAR, port_No(port));
1944 sx_out(bp, CD186x_MSVR, port->MSVR);
1945 spin_unlock_irqrestore(&bp->lock, flags);
1946 spin_unlock_irqrestore(&port->lock, flags);
1947 func_exit();
1948 return 0;
1949}
1950
1951
1952static inline void sx_send_break(struct specialix_port * port, unsigned long length)
1953{
1954 struct specialix_board *bp = port_Board(port);
1955 unsigned long flags;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001956
Linus Torvalds1da177e2005-04-16 15:20:36 -07001957 func_enter();
1958
1959 spin_lock_irqsave (&port->lock, flags);
1960 port->break_length = SPECIALIX_TPS / HZ * length;
1961 port->COR2 |= COR2_ETC;
1962 port->IER |= IER_TXRDY;
1963 spin_lock_irqsave(&bp->lock, flags);
1964 sx_out(bp, CD186x_CAR, port_No(port));
1965 sx_out(bp, CD186x_COR2, port->COR2);
1966 sx_out(bp, CD186x_IER, port->IER);
1967 spin_unlock_irqrestore(&bp->lock, flags);
1968 spin_unlock_irqrestore (&port->lock, flags);
1969 sx_wait_CCR(bp);
1970 spin_lock_irqsave(&bp->lock, flags);
1971 sx_out(bp, CD186x_CCR, CCR_CORCHG2);
1972 spin_unlock_irqrestore(&bp->lock, flags);
1973 sx_wait_CCR(bp);
1974
1975 func_exit();
1976}
1977
1978
1979static inline int sx_set_serial_info(struct specialix_port * port,
1980 struct serial_struct __user * newinfo)
1981{
1982 struct serial_struct tmp;
1983 struct specialix_board *bp = port_Board(port);
1984 int change_speed;
1985
1986 func_enter();
1987 /*
Jesper Juhle49332b2005-05-01 08:59:08 -07001988 if (!access_ok(VERIFY_READ, (void *) newinfo, sizeof(tmp))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001989 func_exit();
Jesper Juhle49332b2005-05-01 08:59:08 -07001990 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001991 }
1992 */
1993 if (copy_from_user(&tmp, newinfo, sizeof(tmp))) {
1994 func_enter();
1995 return -EFAULT;
1996 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001997
1998#if 0
Linus Torvalds1da177e2005-04-16 15:20:36 -07001999 if ((tmp.irq != bp->irq) ||
2000 (tmp.port != bp->base) ||
2001 (tmp.type != PORT_CIRRUS) ||
2002 (tmp.baud_base != (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC) ||
2003 (tmp.custom_divisor != 0) ||
2004 (tmp.xmit_fifo_size != CD186x_NFIFO) ||
2005 (tmp.flags & ~SPECIALIX_LEGAL_FLAGS)) {
2006 func_exit();
2007 return -EINVAL;
2008 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002009#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07002010
2011 change_speed = ((port->flags & ASYNC_SPD_MASK) !=
2012 (tmp.flags & ASYNC_SPD_MASK));
2013 change_speed |= (tmp.custom_divisor != port->custom_divisor);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002014
Linus Torvalds1da177e2005-04-16 15:20:36 -07002015 if (!capable(CAP_SYS_ADMIN)) {
2016 if ((tmp.close_delay != port->close_delay) ||
2017 (tmp.closing_wait != port->closing_wait) ||
2018 ((tmp.flags & ~ASYNC_USR_MASK) !=
2019 (port->flags & ~ASYNC_USR_MASK))) {
2020 func_exit();
2021 return -EPERM;
2022 }
2023 port->flags = ((port->flags & ~ASYNC_USR_MASK) |
2024 (tmp.flags & ASYNC_USR_MASK));
2025 port->custom_divisor = tmp.custom_divisor;
2026 } else {
2027 port->flags = ((port->flags & ~ASYNC_FLAGS) |
2028 (tmp.flags & ASYNC_FLAGS));
2029 port->close_delay = tmp.close_delay;
2030 port->closing_wait = tmp.closing_wait;
2031 port->custom_divisor = tmp.custom_divisor;
2032 }
2033 if (change_speed) {
2034 sx_change_speed(bp, port);
2035 }
2036 func_exit();
2037 return 0;
2038}
2039
2040
2041static inline int sx_get_serial_info(struct specialix_port * port,
2042 struct serial_struct __user *retinfo)
2043{
2044 struct serial_struct tmp;
2045 struct specialix_board *bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002046
Linus Torvalds1da177e2005-04-16 15:20:36 -07002047 func_enter();
2048
2049 /*
Jesper Juhle49332b2005-05-01 08:59:08 -07002050 if (!access_ok(VERIFY_WRITE, (void *) retinfo, sizeof(tmp)))
2051 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002052 */
2053
2054 memset(&tmp, 0, sizeof(tmp));
2055 tmp.type = PORT_CIRRUS;
2056 tmp.line = port - sx_port;
2057 tmp.port = bp->base;
2058 tmp.irq = bp->irq;
2059 tmp.flags = port->flags;
2060 tmp.baud_base = (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC;
2061 tmp.close_delay = port->close_delay * HZ/100;
2062 tmp.closing_wait = port->closing_wait * HZ/100;
2063 tmp.custom_divisor = port->custom_divisor;
2064 tmp.xmit_fifo_size = CD186x_NFIFO;
2065 if (copy_to_user(retinfo, &tmp, sizeof(tmp))) {
2066 func_exit();
2067 return -EFAULT;
2068 }
2069
2070 func_exit();
2071 return 0;
2072}
2073
2074
Jeff Garzikd61780c2005-10-30 15:01:51 -08002075static int sx_ioctl(struct tty_struct * tty, struct file * filp,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002076 unsigned int cmd, unsigned long arg)
2077{
2078 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2079 int retval;
2080 void __user *argp = (void __user *)arg;
2081
2082 func_enter();
2083
2084 if (sx_paranoia_check(port, tty->name, "sx_ioctl")) {
2085 func_exit();
2086 return -ENODEV;
2087 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002088
Linus Torvalds1da177e2005-04-16 15:20:36 -07002089 switch (cmd) {
2090 case TCSBRK: /* SVID version: non-zero arg --> no break */
2091 retval = tty_check_change(tty);
2092 if (retval) {
2093 func_exit();
2094 return retval;
2095 }
2096 tty_wait_until_sent(tty, 0);
2097 if (!arg)
2098 sx_send_break(port, HZ/4); /* 1/4 second */
2099 return 0;
2100 case TCSBRKP: /* support for POSIX tcsendbreak() */
2101 retval = tty_check_change(tty);
2102 if (retval) {
2103 func_exit();
2104 return retval;
2105 }
2106 tty_wait_until_sent(tty, 0);
2107 sx_send_break(port, arg ? arg*(HZ/10) : HZ/4);
2108 func_exit();
2109 return 0;
2110 case TIOCGSOFTCAR:
2111 if (put_user(C_CLOCAL(tty)?1:0, (unsigned long __user *)argp)) {
2112 func_exit();
2113 return -EFAULT;
2114 }
2115 func_exit();
2116 return 0;
2117 case TIOCSSOFTCAR:
2118 if (get_user(arg, (unsigned long __user *) argp)) {
2119 func_exit();
2120 return -EFAULT;
2121 }
2122 tty->termios->c_cflag =
2123 ((tty->termios->c_cflag & ~CLOCAL) |
2124 (arg ? CLOCAL : 0));
2125 func_exit();
2126 return 0;
2127 case TIOCGSERIAL:
2128 func_exit();
2129 return sx_get_serial_info(port, argp);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002130 case TIOCSSERIAL:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002131 func_exit();
2132 return sx_set_serial_info(port, argp);
2133 default:
2134 func_exit();
2135 return -ENOIOCTLCMD;
2136 }
2137 func_exit();
2138 return 0;
2139}
2140
2141
2142static void sx_throttle(struct tty_struct * tty)
2143{
2144 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2145 struct specialix_board *bp;
2146 unsigned long flags;
2147
2148 func_enter();
2149
2150 if (sx_paranoia_check(port, tty->name, "sx_throttle")) {
2151 func_exit();
2152 return;
2153 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002154
Linus Torvalds1da177e2005-04-16 15:20:36 -07002155 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002156
Linus Torvalds1da177e2005-04-16 15:20:36 -07002157 /* Use DTR instead of RTS ! */
Jeff Garzikd61780c2005-10-30 15:01:51 -08002158 if (SX_CRTSCTS (tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002159 port->MSVR &= ~MSVR_DTR;
2160 else {
2161 /* Auch!!! I think the system shouldn't call this then. */
2162 /* Or maybe we're supposed (allowed?) to do our side of hw
Jeff Garzikd61780c2005-10-30 15:01:51 -08002163 handshake anyway, even when hardware handshake is off.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002164 When you see this in your logs, please report.... */
2165 printk (KERN_ERR "sx%d: Need to throttle, but can't (hardware hs is off)\n",
2166 port_No (port));
2167 }
2168 spin_lock_irqsave(&bp->lock, flags);
2169 sx_out(bp, CD186x_CAR, port_No(port));
2170 spin_unlock_irqrestore(&bp->lock, flags);
2171 if (I_IXOFF(tty)) {
2172 spin_unlock_irqrestore(&bp->lock, flags);
2173 sx_wait_CCR(bp);
2174 spin_lock_irqsave(&bp->lock, flags);
2175 sx_out(bp, CD186x_CCR, CCR_SSCH2);
2176 spin_unlock_irqrestore(&bp->lock, flags);
2177 sx_wait_CCR(bp);
2178 }
2179 spin_lock_irqsave(&bp->lock, flags);
2180 sx_out(bp, CD186x_MSVR, port->MSVR);
2181 spin_unlock_irqrestore(&bp->lock, flags);
2182
2183 func_exit();
2184}
2185
2186
2187static void sx_unthrottle(struct tty_struct * tty)
2188{
2189 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2190 struct specialix_board *bp;
2191 unsigned long flags;
2192
2193 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002194
Linus Torvalds1da177e2005-04-16 15:20:36 -07002195 if (sx_paranoia_check(port, tty->name, "sx_unthrottle")) {
2196 func_exit();
2197 return;
2198 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002199
Linus Torvalds1da177e2005-04-16 15:20:36 -07002200 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002201
Linus Torvalds1da177e2005-04-16 15:20:36 -07002202 spin_lock_irqsave(&port->lock, flags);
2203 /* XXXX Use DTR INSTEAD???? */
2204 if (SX_CRTSCTS(tty)) {
2205 port->MSVR |= MSVR_DTR;
2206 } /* Else clause: see remark in "sx_throttle"... */
2207 spin_lock_irqsave(&bp->lock, flags);
2208 sx_out(bp, CD186x_CAR, port_No(port));
2209 spin_unlock_irqrestore(&bp->lock, flags);
2210 if (I_IXOFF(tty)) {
2211 spin_unlock_irqrestore(&port->lock, flags);
2212 sx_wait_CCR(bp);
2213 spin_lock_irqsave(&bp->lock, flags);
2214 sx_out(bp, CD186x_CCR, CCR_SSCH1);
2215 spin_unlock_irqrestore(&bp->lock, flags);
2216 sx_wait_CCR(bp);
2217 spin_lock_irqsave(&port->lock, flags);
2218 }
2219 spin_lock_irqsave(&bp->lock, flags);
2220 sx_out(bp, CD186x_MSVR, port->MSVR);
2221 spin_unlock_irqrestore(&bp->lock, flags);
2222 spin_unlock_irqrestore(&port->lock, flags);
2223
2224 func_exit();
2225}
2226
2227
2228static void sx_stop(struct tty_struct * tty)
2229{
2230 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2231 struct specialix_board *bp;
2232 unsigned long flags;
2233
2234 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002235
Linus Torvalds1da177e2005-04-16 15:20:36 -07002236 if (sx_paranoia_check(port, tty->name, "sx_stop")) {
2237 func_exit();
2238 return;
2239 }
2240
2241 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002242
Linus Torvalds1da177e2005-04-16 15:20:36 -07002243 spin_lock_irqsave(&port->lock, flags);
2244 port->IER &= ~IER_TXRDY;
2245 spin_lock_irqsave(&bp->lock, flags);
2246 sx_out(bp, CD186x_CAR, port_No(port));
2247 sx_out(bp, CD186x_IER, port->IER);
2248 spin_unlock_irqrestore(&bp->lock, flags);
2249 spin_unlock_irqrestore(&port->lock, flags);
2250
2251 func_exit();
2252}
2253
2254
2255static void sx_start(struct tty_struct * tty)
2256{
2257 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2258 struct specialix_board *bp;
2259 unsigned long flags;
2260
2261 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002262
Linus Torvalds1da177e2005-04-16 15:20:36 -07002263 if (sx_paranoia_check(port, tty->name, "sx_start")) {
2264 func_exit();
2265 return;
2266 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002267
Linus Torvalds1da177e2005-04-16 15:20:36 -07002268 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002269
Linus Torvalds1da177e2005-04-16 15:20:36 -07002270 spin_lock_irqsave(&port->lock, flags);
2271 if (port->xmit_cnt && port->xmit_buf && !(port->IER & IER_TXRDY)) {
2272 port->IER |= IER_TXRDY;
2273 spin_lock_irqsave(&bp->lock, flags);
2274 sx_out(bp, CD186x_CAR, port_No(port));
2275 sx_out(bp, CD186x_IER, port->IER);
2276 spin_unlock_irqrestore(&bp->lock, flags);
2277 }
2278 spin_unlock_irqrestore(&port->lock, flags);
2279
2280 func_exit();
2281}
2282
2283
2284/*
2285 * This routine is called from the work-queue when the interrupt
2286 * routine has signalled that a hangup has occurred. The path of
2287 * hangup processing is:
2288 *
2289 * serial interrupt routine -> (workqueue) ->
2290 * do_sx_hangup() -> tty->hangup() -> sx_hangup()
Jeff Garzikd61780c2005-10-30 15:01:51 -08002291 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07002292 */
2293static void do_sx_hangup(void *private_)
2294{
2295 struct specialix_port *port = (struct specialix_port *) private_;
2296 struct tty_struct *tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002297
Linus Torvalds1da177e2005-04-16 15:20:36 -07002298 func_enter();
2299
2300 tty = port->tty;
2301 if (tty)
2302 tty_hangup(tty); /* FIXME: module removal race here */
2303
2304 func_exit();
2305}
2306
2307
2308static void sx_hangup(struct tty_struct * tty)
2309{
2310 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2311 struct specialix_board *bp;
2312 unsigned long flags;
2313
2314 func_enter();
2315
2316 if (sx_paranoia_check(port, tty->name, "sx_hangup")) {
2317 func_exit();
2318 return;
2319 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002320
Linus Torvalds1da177e2005-04-16 15:20:36 -07002321 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002322
Linus Torvalds1da177e2005-04-16 15:20:36 -07002323 sx_shutdown_port(bp, port);
2324 spin_lock_irqsave(&port->lock, flags);
2325 port->event = 0;
2326 bp->count -= port->count;
2327 if (bp->count < 0) {
2328 printk(KERN_ERR "sx%d: sx_hangup: bad board count: %d port: %d\n",
2329 board_No(bp), bp->count, tty->index);
2330 bp->count = 0;
2331 }
2332 port->count = 0;
2333 port->flags &= ~ASYNC_NORMAL_ACTIVE;
2334 port->tty = NULL;
2335 spin_unlock_irqrestore(&port->lock, flags);
2336 wake_up_interruptible(&port->open_wait);
2337
2338 func_exit();
2339}
2340
2341
2342static void sx_set_termios(struct tty_struct * tty, struct termios * old_termios)
2343{
2344 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2345 unsigned long flags;
2346 struct specialix_board * bp;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002347
Linus Torvalds1da177e2005-04-16 15:20:36 -07002348 if (sx_paranoia_check(port, tty->name, "sx_set_termios"))
2349 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002350
Linus Torvalds1da177e2005-04-16 15:20:36 -07002351 if (tty->termios->c_cflag == old_termios->c_cflag &&
2352 tty->termios->c_iflag == old_termios->c_iflag)
2353 return;
2354
2355 bp = port_Board(port);
2356 spin_lock_irqsave(&port->lock, flags);
2357 sx_change_speed(port_Board(port), port);
2358 spin_unlock_irqrestore(&port->lock, flags);
2359
2360 if ((old_termios->c_cflag & CRTSCTS) &&
2361 !(tty->termios->c_cflag & CRTSCTS)) {
2362 tty->hw_stopped = 0;
2363 sx_start(tty);
2364 }
2365}
2366
2367
2368static void do_softint(void *private_)
2369{
2370 struct specialix_port *port = (struct specialix_port *) private_;
2371 struct tty_struct *tty;
2372
2373 func_enter();
2374
2375 if(!(tty = port->tty)) {
2376 func_exit();
2377 return;
2378 }
2379
2380 if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) {
2381 tty_wakeup(tty);
2382 //wake_up_interruptible(&tty->write_wait);
2383 }
2384
2385 func_exit();
2386}
2387
2388static struct tty_operations sx_ops = {
2389 .open = sx_open,
2390 .close = sx_close,
2391 .write = sx_write,
2392 .put_char = sx_put_char,
2393 .flush_chars = sx_flush_chars,
2394 .write_room = sx_write_room,
2395 .chars_in_buffer = sx_chars_in_buffer,
2396 .flush_buffer = sx_flush_buffer,
2397 .ioctl = sx_ioctl,
2398 .throttle = sx_throttle,
2399 .unthrottle = sx_unthrottle,
2400 .set_termios = sx_set_termios,
2401 .stop = sx_stop,
2402 .start = sx_start,
2403 .hangup = sx_hangup,
2404 .tiocmget = sx_tiocmget,
2405 .tiocmset = sx_tiocmset,
2406};
2407
2408static int sx_init_drivers(void)
2409{
2410 int error;
2411 int i;
2412
2413 func_enter();
2414
2415 specialix_driver = alloc_tty_driver(SX_NBOARD * SX_NPORT);
2416 if (!specialix_driver) {
2417 printk(KERN_ERR "sx: Couldn't allocate tty_driver.\n");
2418 func_exit();
2419 return 1;
2420 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002421
Linus Torvalds1da177e2005-04-16 15:20:36 -07002422 if (!(tmp_buf = (unsigned char *) get_zeroed_page(GFP_KERNEL))) {
2423 printk(KERN_ERR "sx: Couldn't get free page.\n");
2424 put_tty_driver(specialix_driver);
2425 func_exit();
2426 return 1;
2427 }
2428 specialix_driver->owner = THIS_MODULE;
2429 specialix_driver->name = "ttyW";
2430 specialix_driver->major = SPECIALIX_NORMAL_MAJOR;
2431 specialix_driver->type = TTY_DRIVER_TYPE_SERIAL;
2432 specialix_driver->subtype = SERIAL_TYPE_NORMAL;
2433 specialix_driver->init_termios = tty_std_termios;
2434 specialix_driver->init_termios.c_cflag =
2435 B9600 | CS8 | CREAD | HUPCL | CLOCAL;
2436 specialix_driver->flags = TTY_DRIVER_REAL_RAW;
2437 tty_set_operations(specialix_driver, &sx_ops);
2438
2439 if ((error = tty_register_driver(specialix_driver))) {
2440 put_tty_driver(specialix_driver);
2441 free_page((unsigned long)tmp_buf);
2442 printk(KERN_ERR "sx: Couldn't register specialix IO8+ driver, error = %d\n",
2443 error);
2444 func_exit();
2445 return 1;
2446 }
2447 memset(sx_port, 0, sizeof(sx_port));
2448 for (i = 0; i < SX_NPORT * SX_NBOARD; i++) {
2449 sx_port[i].magic = SPECIALIX_MAGIC;
2450 INIT_WORK(&sx_port[i].tqueue, do_softint, &sx_port[i]);
2451 INIT_WORK(&sx_port[i].tqueue_hangup, do_sx_hangup, &sx_port[i]);
2452 sx_port[i].close_delay = 50 * HZ/100;
2453 sx_port[i].closing_wait = 3000 * HZ/100;
2454 init_waitqueue_head(&sx_port[i].open_wait);
2455 init_waitqueue_head(&sx_port[i].close_wait);
2456 spin_lock_init(&sx_port[i].lock);
2457 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002458
Linus Torvalds1da177e2005-04-16 15:20:36 -07002459 func_exit();
2460 return 0;
2461}
2462
2463static void sx_release_drivers(void)
2464{
2465 func_enter();
2466
2467 free_page((unsigned long)tmp_buf);
2468 tty_unregister_driver(specialix_driver);
2469 put_tty_driver(specialix_driver);
2470 func_exit();
2471}
2472
Jeff Garzikd61780c2005-10-30 15:01:51 -08002473/*
2474 * This routine must be called by kernel at boot time
Linus Torvalds1da177e2005-04-16 15:20:36 -07002475 */
2476static int __init specialix_init(void)
2477{
2478 int i;
2479 int found = 0;
2480
2481 func_enter();
2482
2483 printk(KERN_INFO "sx: Specialix IO8+ driver v" VERSION ", (c) R.E.Wolff 1997/1998.\n");
2484 printk(KERN_INFO "sx: derived from work (c) D.Gorodchanin 1994-1996.\n");
2485#ifdef CONFIG_SPECIALIX_RTSCTS
2486 printk (KERN_INFO "sx: DTR/RTS pin is always RTS.\n");
2487#else
2488 printk (KERN_INFO "sx: DTR/RTS pin is RTS when CRTSCTS is on.\n");
2489#endif
Jeff Garzikd61780c2005-10-30 15:01:51 -08002490
Linus Torvalds1da177e2005-04-16 15:20:36 -07002491 for (i = 0; i < SX_NBOARD; i++)
2492 sx_board[i].lock = SPIN_LOCK_UNLOCKED;
2493
2494 if (sx_init_drivers()) {
2495 func_exit();
2496 return -EIO;
2497 }
2498
Jeff Garzikd61780c2005-10-30 15:01:51 -08002499 for (i = 0; i < SX_NBOARD; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002500 if (sx_board[i].base && !sx_probe(&sx_board[i]))
2501 found++;
2502
2503#ifdef CONFIG_PCI
2504 {
2505 struct pci_dev *pdev = NULL;
2506
2507 i=0;
2508 while (i < SX_NBOARD) {
2509 if (sx_board[i].flags & SX_BOARD_PRESENT) {
2510 i++;
2511 continue;
2512 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002513 pdev = pci_find_device (PCI_VENDOR_ID_SPECIALIX,
2514 PCI_DEVICE_ID_SPECIALIX_IO8,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002515 pdev);
2516 if (!pdev) break;
2517
2518 if (pci_enable_device(pdev))
2519 continue;
2520
2521 sx_board[i].irq = pdev->irq;
2522
2523 sx_board[i].base = pci_resource_start (pdev, 2);
2524
2525 sx_board[i].flags |= SX_BOARD_IS_PCI;
2526 if (!sx_probe(&sx_board[i]))
2527 found ++;
2528 }
2529 }
2530#endif
2531
2532 if (!found) {
2533 sx_release_drivers();
2534 printk(KERN_INFO "sx: No specialix IO8+ boards detected.\n");
2535 func_exit();
2536 return -EIO;
2537 }
2538
2539 func_exit();
2540 return 0;
2541}
2542
2543static int iobase[SX_NBOARD] = {0,};
2544
2545static int irq [SX_NBOARD] = {0,};
2546
2547module_param_array(iobase, int, NULL, 0);
2548module_param_array(irq, int, NULL, 0);
2549module_param(sx_debug, int, 0);
2550module_param(sx_rxfifo, int, 0);
2551#ifdef SPECIALIX_TIMER
2552module_param(sx_poll, int, 0);
2553#endif
2554
2555/*
2556 * You can setup up to 4 boards.
2557 * by specifying "iobase=0xXXX,0xXXX ..." as insmod parameter.
Jeff Garzikd61780c2005-10-30 15:01:51 -08002558 * You should specify the IRQs too in that case "irq=....,...".
2559 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07002560 * More than 4 boards in one computer is not possible, as the card can
Jeff Garzikd61780c2005-10-30 15:01:51 -08002561 * only use 4 different interrupts.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002562 *
2563 */
2564static int __init specialix_init_module(void)
2565{
2566 int i;
2567
2568 func_enter();
2569
2570 init_MUTEX(&tmp_buf_sem); /* Init de the semaphore - pvdl */
2571
2572 if (iobase[0] || iobase[1] || iobase[2] || iobase[3]) {
2573 for(i = 0; i < SX_NBOARD; i++) {
2574 sx_board[i].base = iobase[i];
2575 sx_board[i].irq = irq[i];
2576 sx_board[i].count= 0;
2577 }
2578 }
2579
2580 func_exit();
2581
2582 return specialix_init();
2583}
Jeff Garzikd61780c2005-10-30 15:01:51 -08002584
Linus Torvalds1da177e2005-04-16 15:20:36 -07002585static void __exit specialix_exit_module(void)
2586{
2587 int i;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002588
Linus Torvalds1da177e2005-04-16 15:20:36 -07002589 func_enter();
2590
2591 sx_release_drivers();
2592 for (i = 0; i < SX_NBOARD; i++)
Jeff Garzikd61780c2005-10-30 15:01:51 -08002593 if (sx_board[i].flags & SX_BOARD_PRESENT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002594 sx_release_io_range(&sx_board[i]);
2595#ifdef SPECIALIX_TIMER
2596 del_timer (&missed_irq_timer);
2597#endif
2598
2599 func_exit();
2600}
2601
2602module_init(specialix_init_module);
2603module_exit(specialix_exit_module);
2604
2605MODULE_LICENSE("GPL");