blob: c390c6cc030e2caaeab5df7ce0e104a306b67eb3 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * specialix.c -- specialix IO8+ multiport serial driver.
3 *
4 * Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl)
5 * Copyright (C) 1994-1996 Dmitry Gorodchanin (pgmdsg@ibi.com)
6 *
7 * Specialix pays for the development and support of this driver.
8 * Please DO contact io8-linux@specialix.co.uk if you require
9 * support. But please read the documentation (specialix.txt)
10 * first.
11 *
12 * This driver was developped in the BitWizard linux device
13 * driver service. If you require a linux device driver for your
14 * product, please contact devices@BitWizard.nl for a quote.
15 *
16 * This code is firmly based on the riscom/8 serial driver,
17 * written by Dmitry Gorodchanin. The specialix IO8+ card
18 * programming information was obtained from the CL-CD1865 Data
19 * Book, and Specialix document number 6200059: IO8+ Hardware
20 * Functional Specification.
21 *
22 * This program is free software; you can redistribute it and/or
23 * modify it under the terms of the GNU General Public License as
24 * published by the Free Software Foundation; either version 2 of
25 * the License, or (at your option) any later version.
26 *
27 * This program is distributed in the hope that it will be
28 * useful, but WITHOUT ANY WARRANTY; without even the implied
29 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
30 * PURPOSE. See the GNU General Public License for more details.
31 *
32 * You should have received a copy of the GNU General Public
33 * License along with this program; if not, write to the Free
34 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
35 * USA.
36 *
37 * Revision history:
38 *
39 * Revision 1.0: April 1st 1997.
40 * Initial release for alpha testing.
Jeff Garzikd61780c2005-10-30 15:01:51 -080041 * Revision 1.1: April 14th 1997.
42 * Incorporated Richard Hudsons suggestions,
Linus Torvalds1da177e2005-04-16 15:20:36 -070043 * removed some debugging printk's.
44 * Revision 1.2: April 15th 1997.
45 * Ported to 2.1.x kernels.
Jeff Garzikd61780c2005-10-30 15:01:51 -080046 * Revision 1.3: April 17th 1997
47 * Backported to 2.0. (Compatibility macros).
Linus Torvalds1da177e2005-04-16 15:20:36 -070048 * Revision 1.4: April 18th 1997
Jeff Garzikd61780c2005-10-30 15:01:51 -080049 * Fixed DTR/RTS bug that caused the card to indicate
50 * "don't send data" to a modem after the password prompt.
Linus Torvalds1da177e2005-04-16 15:20:36 -070051 * Fixed bug for premature (fake) interrupts.
52 * Revision 1.5: April 19th 1997
Jeff Garzikd61780c2005-10-30 15:01:51 -080053 * fixed a minor typo in the header file, cleanup a little.
Linus Torvalds1da177e2005-04-16 15:20:36 -070054 * performance warnings are now MAXed at once per minute.
55 * Revision 1.6: May 23 1997
56 * Changed the specialix=... format to include interrupt.
57 * Revision 1.7: May 27 1997
58 * Made many more debug printk's a compile time option.
59 * Revision 1.8: Jul 1 1997
60 * port to linux-2.1.43 kernel.
61 * Revision 1.9: Oct 9 1998
62 * Added stuff for the IO8+/PCI version.
Jeff Garzikd61780c2005-10-30 15:01:51 -080063 * Revision 1.10: Oct 22 1999 / Jan 21 2000.
64 * Added stuff for setserial.
Linus Torvalds1da177e2005-04-16 15:20:36 -070065 * Nicolas Mailhot (Nicolas.Mailhot@email.enst.fr)
Jeff Garzikd61780c2005-10-30 15:01:51 -080066 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070067 */
68
69#define VERSION "1.11"
70
71
72/*
73 * There is a bunch of documentation about the card, jumpers, config
74 * settings, restrictions, cables, device names and numbers in
75 * Documentation/specialix.txt
76 */
77
Linus Torvalds1da177e2005-04-16 15:20:36 -070078#include <linux/module.h>
79
Alan Coxa72492b2008-07-22 11:17:05 +010080#include <linux/io.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070081#include <linux/kernel.h>
82#include <linux/sched.h>
83#include <linux/ioport.h>
84#include <linux/interrupt.h>
85#include <linux/errno.h>
86#include <linux/tty.h>
Alan Cox33f0f882006-01-09 20:54:13 -080087#include <linux/tty_flip.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070088#include <linux/mm.h>
89#include <linux/serial.h>
90#include <linux/fcntl.h>
91#include <linux/major.h>
92#include <linux/delay.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070093#include <linux/pci.h>
94#include <linux/init.h>
Alan Coxa72492b2008-07-22 11:17:05 +010095#include <linux/uaccess.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070096
97#include "specialix_io8.h"
98#include "cd1865.h"
99
100
101/*
102 This driver can spew a whole lot of debugging output at you. If you
103 need maximum performance, you should disable the DEBUG define. To
104 aid in debugging in the field, I'm leaving the compile-time debug
105 features enabled, and disable them "runtime". That allows me to
106 instruct people with problems to enable debugging without requiring
107 them to recompile...
108*/
109#define DEBUG
110
111static int sx_debug;
112static int sx_rxfifo = SPECIALIX_RXFIFO;
113
114#ifdef DEBUG
Alan Coxa72492b2008-07-22 11:17:05 +0100115#define dprintk(f, str...) if (sx_debug & f) printk(str)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116#else
117#define dprintk(f, str...) /* nothing */
118#endif
119
120#define SX_DEBUG_FLOW 0x0001
121#define SX_DEBUG_DATA 0x0002
122#define SX_DEBUG_PROBE 0x0004
123#define SX_DEBUG_CHAN 0x0008
124#define SX_DEBUG_INIT 0x0010
125#define SX_DEBUG_RX 0x0020
126#define SX_DEBUG_TX 0x0040
127#define SX_DEBUG_IRQ 0x0080
128#define SX_DEBUG_OPEN 0x0100
129#define SX_DEBUG_TERMIOS 0x0200
130#define SX_DEBUG_SIGNALS 0x0400
131#define SX_DEBUG_FIFO 0x0800
132
133
Alan Coxa72492b2008-07-22 11:17:05 +0100134#define func_enter() dprintk(SX_DEBUG_FLOW, "io8: enter %s\n", __func__)
135#define func_exit() dprintk(SX_DEBUG_FLOW, "io8: exit %s\n", __func__)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136
137#define jiffies_from_ms(a) ((((a) * HZ)/1000)+1)
138
139
140/* Configurable options: */
141
142/* Am I paranoid or not ? ;-) */
143#define SPECIALIX_PARANOIA_CHECK
144
145/* Do I trust the IRQ from the card? (enabeling it doesn't seem to help)
146 When the IRQ routine leaves the chip in a state that is keeps on
147 requiring attention, the timer doesn't help either. */
148#undef SPECIALIX_TIMER
149
150#ifdef SPECIALIX_TIMER
151static int sx_poll = HZ;
152#endif
153
154
155
Jeff Garzikd61780c2005-10-30 15:01:51 -0800156/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157 * The following defines are mostly for testing purposes. But if you need
158 * some nice reporting in your syslog, you can define them also.
159 */
160#undef SX_REPORT_FIFO
161#undef SX_REPORT_OVERRUN
162
163
164
165#ifdef CONFIG_SPECIALIX_RTSCTS
166#define SX_CRTSCTS(bla) 1
167#else
168#define SX_CRTSCTS(tty) C_CRTSCTS(tty)
169#endif
170
171
Alan Coxa72492b2008-07-22 11:17:05 +0100172/* Used to be outb(0xff, 0x80); */
173#define short_pause() udelay(1)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174
175
176#define SPECIALIX_LEGAL_FLAGS \
177 (ASYNC_HUP_NOTIFY | ASYNC_SAK | ASYNC_SPLIT_TERMIOS | \
178 ASYNC_SPD_HI | ASYNC_SPEED_VHI | ASYNC_SESSION_LOCKOUT | \
179 ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP)
180
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181static struct tty_driver *specialix_driver;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183static struct specialix_board sx_board[SX_NBOARD] = {
184 { 0, SX_IOBASE1, 9, },
185 { 0, SX_IOBASE2, 11, },
186 { 0, SX_IOBASE3, 12, },
187 { 0, SX_IOBASE4, 15, },
188};
189
190static struct specialix_port sx_port[SX_NBOARD * SX_NPORT];
191
192
193#ifdef SPECIALIX_TIMER
194static struct timer_list missed_irq_timer;
Alan Coxa72492b2008-07-22 11:17:05 +0100195static irqreturn_t sx_interrupt(int irq, void *dev_id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196#endif
197
198
199
Alan Coxa72492b2008-07-22 11:17:05 +0100200static inline int sx_paranoia_check(struct specialix_port const *port,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201 char *name, const char *routine)
202{
203#ifdef SPECIALIX_PARANOIA_CHECK
Alan Coxa72492b2008-07-22 11:17:05 +0100204 static const char *badmagic = KERN_ERR
205 "sx: Warning: bad specialix port magic number for device %s in %s\n";
206 static const char *badinfo = KERN_ERR
207 "sx: Warning: null specialix port for device %s in %s\n";
Jeff Garzikd61780c2005-10-30 15:01:51 -0800208
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209 if (!port) {
210 printk(badinfo, name, routine);
211 return 1;
212 }
213 if (port->magic != SPECIALIX_MAGIC) {
214 printk(badmagic, name, routine);
215 return 1;
216 }
217#endif
218 return 0;
219}
220
221
222/*
Jeff Garzikd61780c2005-10-30 15:01:51 -0800223 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 * Service functions for specialix IO8+ driver.
Jeff Garzikd61780c2005-10-30 15:01:51 -0800225 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226 */
227
228/* Get board number from pointer */
Alan Coxa72492b2008-07-22 11:17:05 +0100229static inline int board_No(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230{
231 return bp - sx_board;
232}
233
234
235/* Get port number from pointer */
Alan Coxa72492b2008-07-22 11:17:05 +0100236static inline int port_No(struct specialix_port const *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237{
Jeff Garzikd61780c2005-10-30 15:01:51 -0800238 return SX_PORT(port - sx_port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239}
240
241
242/* Get pointer to board from pointer to port */
Alan Coxa72492b2008-07-22 11:17:05 +0100243static inline struct specialix_board *port_Board(
244 struct specialix_port const *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245{
246 return &sx_board[SX_BOARD(port - sx_port)];
247}
248
249
250/* Input Byte from CL CD186x register */
Alan Coxa72492b2008-07-22 11:17:05 +0100251static inline unsigned char sx_in(struct specialix_board *bp,
252 unsigned short reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253{
254 bp->reg = reg | 0x80;
Alan Coxa72492b2008-07-22 11:17:05 +0100255 outb(reg | 0x80, bp->base + SX_ADDR_REG);
256 return inb(bp->base + SX_DATA_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257}
258
259
260/* Output Byte to CL CD186x register */
Alan Coxa72492b2008-07-22 11:17:05 +0100261static inline void sx_out(struct specialix_board *bp, unsigned short reg,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262 unsigned char val)
263{
264 bp->reg = reg | 0x80;
Alan Coxa72492b2008-07-22 11:17:05 +0100265 outb(reg | 0x80, bp->base + SX_ADDR_REG);
266 outb(val, bp->base + SX_DATA_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267}
268
269
270/* Input Byte from CL CD186x register */
Alan Coxa72492b2008-07-22 11:17:05 +0100271static inline unsigned char sx_in_off(struct specialix_board *bp,
272 unsigned short reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273{
274 bp->reg = reg;
Alan Coxa72492b2008-07-22 11:17:05 +0100275 outb(reg, bp->base + SX_ADDR_REG);
276 return inb(bp->base + SX_DATA_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277}
278
279
280/* Output Byte to CL CD186x register */
Alan Coxa72492b2008-07-22 11:17:05 +0100281static inline void sx_out_off(struct specialix_board *bp,
282 unsigned short reg, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283{
284 bp->reg = reg;
Alan Coxa72492b2008-07-22 11:17:05 +0100285 outb(reg, bp->base + SX_ADDR_REG);
286 outb(val, bp->base + SX_DATA_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287}
288
289
290/* Wait for Channel Command Register ready */
Alan Coxa72492b2008-07-22 11:17:05 +0100291static inline void sx_wait_CCR(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292{
293 unsigned long delay, flags;
294 unsigned char ccr;
295
296 for (delay = SX_CCR_TIMEOUT; delay; delay--) {
297 spin_lock_irqsave(&bp->lock, flags);
298 ccr = sx_in(bp, CD186x_CCR);
299 spin_unlock_irqrestore(&bp->lock, flags);
300 if (!ccr)
301 return;
Alan Coxa72492b2008-07-22 11:17:05 +0100302 udelay(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800304
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305 printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp));
306}
307
308
309/* Wait for Channel Command Register ready */
Alan Coxa72492b2008-07-22 11:17:05 +0100310static inline void sx_wait_CCR_off(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311{
312 unsigned long delay;
313 unsigned char crr;
314 unsigned long flags;
315
316 for (delay = SX_CCR_TIMEOUT; delay; delay--) {
317 spin_lock_irqsave(&bp->lock, flags);
318 crr = sx_in_off(bp, CD186x_CCR);
319 spin_unlock_irqrestore(&bp->lock, flags);
320 if (!crr)
321 return;
Alan Coxa72492b2008-07-22 11:17:05 +0100322 udelay(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800324
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp));
326}
327
328
329/*
330 * specialix IO8+ IO range functions.
331 */
332
Alan Coxa72492b2008-07-22 11:17:05 +0100333static inline int sx_request_io_range(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334{
Jeff Garzikd61780c2005-10-30 15:01:51 -0800335 return request_region(bp->base,
336 bp->flags & SX_BOARD_IS_PCI ? SX_PCI_IO_SPACE : SX_IO_SPACE,
337 "specialix IO8+") == NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338}
339
340
Alan Coxa72492b2008-07-22 11:17:05 +0100341static inline void sx_release_io_range(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342{
Alan Coxa72492b2008-07-22 11:17:05 +0100343 release_region(bp->base, bp->flags & SX_BOARD_IS_PCI ?
344 SX_PCI_IO_SPACE : SX_IO_SPACE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345}
346
Jeff Garzikd61780c2005-10-30 15:01:51 -0800347
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348/* Set the IRQ using the RTS lines that run to the PAL on the board.... */
Alan Coxa72492b2008-07-22 11:17:05 +0100349static int sx_set_irq(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350{
351 int virq;
352 int i;
353 unsigned long flags;
354
Jeff Garzikd61780c2005-10-30 15:01:51 -0800355 if (bp->flags & SX_BOARD_IS_PCI)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 return 1;
357 switch (bp->irq) {
358 /* In the same order as in the docs... */
Alan Coxa72492b2008-07-22 11:17:05 +0100359 case 15:
360 virq = 0;
361 break;
362 case 12:
363 virq = 1;
364 break;
365 case 11:
366 virq = 2;
367 break;
368 case 9:
369 virq = 3;
370 break;
371 default:printk(KERN_ERR
372 "Speclialix: cannot set irq to %d.\n", bp->irq);
373 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374 }
375 spin_lock_irqsave(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +0100376 for (i = 0; i < 2; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377 sx_out(bp, CD186x_CAR, i);
378 sx_out(bp, CD186x_MSVRTS, ((virq >> i) & 0x1)? MSVR_RTS:0);
379 }
380 spin_unlock_irqrestore(&bp->lock, flags);
381 return 1;
382}
383
384
385/* Reset and setup CD186x chip */
Alan Coxa72492b2008-07-22 11:17:05 +0100386static int sx_init_CD186x(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387{
388 unsigned long flags;
389 int scaler;
390 int rv = 1;
391
392 func_enter();
393 sx_wait_CCR_off(bp); /* Wait for CCR ready */
394 spin_lock_irqsave(&bp->lock, flags);
395 sx_out_off(bp, CD186x_CCR, CCR_HARDRESET); /* Reset CD186x chip */
396 spin_unlock_irqrestore(&bp->lock, flags);
Jiri Slaby3e98cee2007-07-17 04:05:19 -0700397 msleep(50); /* Delay 0.05 sec */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398 spin_lock_irqsave(&bp->lock, flags);
399 sx_out_off(bp, CD186x_GIVR, SX_ID); /* Set ID for this chip */
400 sx_out_off(bp, CD186x_GICR, 0); /* Clear all bits */
401 sx_out_off(bp, CD186x_PILR1, SX_ACK_MINT); /* Prio for modem intr */
402 sx_out_off(bp, CD186x_PILR2, SX_ACK_TINT); /* Prio for transmitter intr */
403 sx_out_off(bp, CD186x_PILR3, SX_ACK_RINT); /* Prio for receiver intr */
404 /* Set RegAckEn */
Alan Coxa72492b2008-07-22 11:17:05 +0100405 sx_out_off(bp, CD186x_SRCR, sx_in(bp, CD186x_SRCR) | SRCR_REGACKEN);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800406
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407 /* Setting up prescaler. We need 4 ticks per 1 ms */
408 scaler = SX_OSCFREQ/SPECIALIX_TPS;
409
410 sx_out_off(bp, CD186x_PPRH, scaler >> 8);
411 sx_out_off(bp, CD186x_PPRL, scaler & 0xff);
412 spin_unlock_irqrestore(&bp->lock, flags);
413
Alan Coxa72492b2008-07-22 11:17:05 +0100414 if (!sx_set_irq(bp)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415 /* Figure out how to pass this along... */
Alan Coxa72492b2008-07-22 11:17:05 +0100416 printk(KERN_ERR "Cannot set irq to %d.\n", bp->irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417 rv = 0;
418 }
419
420 func_exit();
421 return rv;
422}
423
424
Alan Coxa72492b2008-07-22 11:17:05 +0100425static int read_cross_byte(struct specialix_board *bp, int reg, int bit)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426{
427 int i;
428 int t;
429 unsigned long flags;
430
431 spin_lock_irqsave(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +0100432 for (i = 0, t = 0; i < 8; i++) {
433 sx_out_off(bp, CD186x_CAR, i);
434 if (sx_in_off(bp, reg) & bit)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435 t |= 1 << i;
436 }
437 spin_unlock_irqrestore(&bp->lock, flags);
438
439 return t;
440}
441
442
443#ifdef SPECIALIX_TIMER
Alan Coxa72492b2008-07-22 11:17:05 +0100444void missed_irq(unsigned long data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445{
446 unsigned char irq;
447 unsigned long flags;
448 struct specialix_board *bp = (struct specialix_board *)data;
449
450 spin_lock_irqsave(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +0100451 irq = sx_in((struct specialix_board *)data, CD186x_SRSR) &
452 (SRSR_RREQint | SRSR_TREQint | SRSR_MREQint);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453 spin_unlock_irqrestore(&bp->lock, flags);
454 if (irq) {
Alan Coxa72492b2008-07-22 11:17:05 +0100455 printk(KERN_INFO
456 "Missed interrupt... Calling int from timer. \n");
457 sx_interrupt(-1, bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458 }
Jiri Slaby40565f12007-02-12 00:52:31 -0800459 mod_timer(&missed_irq_timer, jiffies + sx_poll);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460}
461#endif
462
463
464
465/* Main probing routine, also sets irq. */
466static int sx_probe(struct specialix_board *bp)
467{
468 unsigned char val1, val2;
469#if 0
470 int irqs = 0;
471 int retries;
472#endif
473 int rev;
474 int chip;
475
476 func_enter();
477
Jeff Garzikd61780c2005-10-30 15:01:51 -0800478 if (sx_request_io_range(bp)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479 func_exit();
480 return 1;
481 }
482
483 /* Are the I/O ports here ? */
484 sx_out_off(bp, CD186x_PPRL, 0x5a);
Alan Coxa72492b2008-07-22 11:17:05 +0100485 short_pause();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486 val1 = sx_in_off(bp, CD186x_PPRL);
487
488 sx_out_off(bp, CD186x_PPRL, 0xa5);
Alan Coxa72492b2008-07-22 11:17:05 +0100489 short_pause();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490 val2 = sx_in_off(bp, CD186x_PPRL);
491
Jeff Garzikd61780c2005-10-30 15:01:51 -0800492
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493 if ((val1 != 0x5a) || (val2 != 0xa5)) {
Alan Coxa72492b2008-07-22 11:17:05 +0100494 printk(KERN_INFO
495 "sx%d: specialix IO8+ Board at 0x%03x not found.\n",
496 board_No(bp), bp->base);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800497 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498 func_exit();
499 return 1;
500 }
501
Jeff Garzikd61780c2005-10-30 15:01:51 -0800502 /* Check the DSR lines that Specialix uses as board
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503 identification */
Alan Coxa72492b2008-07-22 11:17:05 +0100504 val1 = read_cross_byte(bp, CD186x_MSVR, MSVR_DSR);
505 val2 = read_cross_byte(bp, CD186x_MSVR, MSVR_RTS);
506 dprintk(SX_DEBUG_INIT,
507 "sx%d: DSR lines are: %02x, rts lines are: %02x\n",
508 board_No(bp), val1, val2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509
510 /* They managed to switch the bit order between the docs and
511 the IO8+ card. The new PCI card now conforms to old docs.
512 They changed the PCI docs to reflect the situation on the
513 old card. */
514 val2 = (bp->flags & SX_BOARD_IS_PCI)?0x4d : 0xb2;
515 if (val1 != val2) {
Alan Coxa72492b2008-07-22 11:17:05 +0100516 printk(KERN_INFO
517 "sx%d: specialix IO8+ ID %02x at 0x%03x not found (%02x).\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 board_No(bp), val2, bp->base, val1);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800519 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 func_exit();
521 return 1;
522 }
523
524
525#if 0
526 /* It's time to find IRQ for this board */
527 for (retries = 0; retries < 5 && irqs <= 0; retries++) {
528 irqs = probe_irq_on();
Alan Coxa72492b2008-07-22 11:17:05 +0100529 sx_init_CD186x(bp); /* Reset CD186x chip */
530 sx_out(bp, CD186x_CAR, 2); /* Select port 2 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 sx_wait_CCR(bp);
Alan Coxa72492b2008-07-22 11:17:05 +0100532 sx_out(bp, CD186x_CCR, CCR_TXEN); /* Enable transmitter */
533 sx_out(bp, CD186x_IER, IER_TXRDY); /* Enable tx empty intr */
Jiri Slaby3e98cee2007-07-17 04:05:19 -0700534 msleep(50);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 irqs = probe_irq_off(irqs);
536
Alan Coxa72492b2008-07-22 11:17:05 +0100537 dprintk(SX_DEBUG_INIT, "SRSR = %02x, ", sx_in(bp, CD186x_SRSR));
538 dprintk(SX_DEBUG_INIT, "TRAR = %02x, ", sx_in(bp, CD186x_TRAR));
539 dprintk(SX_DEBUG_INIT, "GIVR = %02x, ", sx_in(bp, CD186x_GIVR));
540 dprintk(SX_DEBUG_INIT, "GICR = %02x, ", sx_in(bp, CD186x_GICR));
541 dprintk(SX_DEBUG_INIT, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542
543 /* Reset CD186x again */
544 if (!sx_init_CD186x(bp)) {
545 /* Hmmm. This is dead code anyway. */
546 }
547
Alan Coxa72492b2008-07-22 11:17:05 +0100548 dprintk(SX_DEBUG_INIT
549 "val1 = %02x, val2 = %02x, val3 = %02x.\n",
550 val1, val2, val3);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800551
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800553
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554#if 0
555 if (irqs <= 0) {
Alan Coxa72492b2008-07-22 11:17:05 +0100556 printk(KERN_ERR
557 "sx%d: Can't find IRQ for specialix IO8+ board at 0x%03x.\n",
558 board_No(bp), bp->base);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800559 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560 func_exit();
561 return 1;
562 }
563#endif
Alan Coxa72492b2008-07-22 11:17:05 +0100564 printk(KERN_INFO "Started with irq=%d, but now have irq=%d.\n",
565 bp->irq, irqs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 if (irqs > 0)
567 bp->irq = irqs;
568#endif
569 /* Reset CD186x again */
570 if (!sx_init_CD186x(bp)) {
Jeff Garzikd61780c2005-10-30 15:01:51 -0800571 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572 func_exit();
Jeff Garzikd61780c2005-10-30 15:01:51 -0800573 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574 }
575
576 sx_request_io_range(bp);
577 bp->flags |= SX_BOARD_PRESENT;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800578
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 /* Chip revcode pkgtype
Alan Coxa72492b2008-07-22 11:17:05 +0100580 GFRCR SRCR bit 7
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581 CD180 rev B 0x81 0
582 CD180 rev C 0x82 0
583 CD1864 rev A 0x82 1
Jeff Garzikd61780c2005-10-30 15:01:51 -0800584 CD1865 rev A 0x83 1 -- Do not use!!! Does not work.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585 CD1865 rev B 0x84 1
586 -- Thanks to Gwen Wang, Cirrus Logic.
587 */
588
589 switch (sx_in_off(bp, CD186x_GFRCR)) {
Alan Coxa72492b2008-07-22 11:17:05 +0100590 case 0x82:
591 chip = 1864;
592 rev = 'A';
593 break;
594 case 0x83:
595 chip = 1865;
596 rev = 'A';
597 break;
598 case 0x84:
599 chip = 1865;
600 rev = 'B';
601 break;
602 case 0x85:
603 chip = 1865;
604 rev = 'C';
605 break; /* Does not exist at this time */
606 default:
607 chip = -1;
608 rev = 'x';
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609 }
610
Alan Coxa72492b2008-07-22 11:17:05 +0100611 dprintk(SX_DEBUG_INIT, " GFCR = 0x%02x\n", sx_in_off(bp, CD186x_GFRCR));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612
613#ifdef SPECIALIX_TIMER
Jiri Slaby40565f12007-02-12 00:52:31 -0800614 setup_timer(&missed_irq_timer, missed_irq, (unsigned long)bp);
615 mod_timer(&missed_irq_timer, jiffies + sx_poll);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616#endif
617
Alan Coxa72492b2008-07-22 11:17:05 +0100618 printk(KERN_INFO
619 "sx%d: specialix IO8+ board detected at 0x%03x, IRQ %d, CD%d Rev. %c.\n",
620 board_No(bp), bp->base, bp->irq, chip, rev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621
622 func_exit();
623 return 0;
624}
625
Jeff Garzikd61780c2005-10-30 15:01:51 -0800626/*
627 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628 * Interrupt processing routines.
629 * */
630
Alan Coxa72492b2008-07-22 11:17:05 +0100631static inline struct specialix_port *sx_get_port(struct specialix_board *bp,
632 unsigned char const *what)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633{
634 unsigned char channel;
Alan Coxa72492b2008-07-22 11:17:05 +0100635 struct specialix_port *port = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636
637 channel = sx_in(bp, CD186x_GICR) >> GICR_CHAN_OFF;
Alan Coxa72492b2008-07-22 11:17:05 +0100638 dprintk(SX_DEBUG_CHAN, "channel: %d\n", channel);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639 if (channel < CD186x_NCH) {
640 port = &sx_port[board_No(bp) * SX_NPORT + channel];
Alan Coxa72492b2008-07-22 11:17:05 +0100641 dprintk(SX_DEBUG_CHAN, "port: %d %p flags: 0x%lx\n",
642 board_No(bp) * SX_NPORT + channel, port,
643 port->port.flags & ASYNC_INITIALIZED);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644
Alan Cox44b7d1b2008-07-16 21:57:18 +0100645 if (port->port.flags & ASYNC_INITIALIZED) {
Alan Coxa72492b2008-07-22 11:17:05 +0100646 dprintk(SX_DEBUG_CHAN, "port: %d %p\n", channel, port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647 func_exit();
648 return port;
649 }
650 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800651 printk(KERN_INFO "sx%d: %s interrupt from invalid port %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652 board_No(bp), what, channel);
653 return NULL;
654}
655
656
Alan Coxa72492b2008-07-22 11:17:05 +0100657static inline void sx_receive_exc(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658{
659 struct specialix_port *port;
660 struct tty_struct *tty;
661 unsigned char status;
Alan Cox33f0f882006-01-09 20:54:13 -0800662 unsigned char ch, flag;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663
664 func_enter();
665
666 port = sx_get_port(bp, "Receive");
667 if (!port) {
Alan Coxa72492b2008-07-22 11:17:05 +0100668 dprintk(SX_DEBUG_RX, "Hmm, couldn't find port.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669 func_exit();
670 return;
671 }
Alan Cox44b7d1b2008-07-16 21:57:18 +0100672 tty = port->port.tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800673
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674 status = sx_in(bp, CD186x_RCSR);
675
Alan Coxa72492b2008-07-22 11:17:05 +0100676 dprintk(SX_DEBUG_RX, "status: 0x%x\n", status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677 if (status & RCSR_OE) {
678 port->overrun++;
Alan Coxa72492b2008-07-22 11:17:05 +0100679 dprintk(SX_DEBUG_FIFO,
680 "sx%d: port %d: Overrun. Total %ld overruns.\n",
681 board_No(bp), port_No(port), port->overrun);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682 }
683 status &= port->mark_mask;
684
685 /* This flip buffer check needs to be below the reading of the
686 status register to reset the chip's IRQ.... */
Alan Cox33f0f882006-01-09 20:54:13 -0800687 if (tty_buffer_request_room(tty, 1) == 0) {
Alan Coxa72492b2008-07-22 11:17:05 +0100688 dprintk(SX_DEBUG_FIFO,
689 "sx%d: port %d: Working around flip buffer overflow.\n",
690 board_No(bp), port_No(port));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691 func_exit();
692 return;
693 }
694
695 ch = sx_in(bp, CD186x_RDR);
696 if (!status) {
697 func_exit();
698 return;
699 }
700 if (status & RCSR_TOUT) {
Alan Coxa72492b2008-07-22 11:17:05 +0100701 printk(KERN_INFO
702 "sx%d: port %d: Receiver timeout. Hardware problems ?\n",
703 board_No(bp), port_No(port));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704 func_exit();
705 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800706
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707 } else if (status & RCSR_BREAK) {
708 dprintk(SX_DEBUG_RX, "sx%d: port %d: Handling break...\n",
709 board_No(bp), port_No(port));
Alan Cox33f0f882006-01-09 20:54:13 -0800710 flag = TTY_BREAK;
Alan Cox44b7d1b2008-07-16 21:57:18 +0100711 if (port->port.flags & ASYNC_SAK)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712 do_SAK(tty);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800713
714 } else if (status & RCSR_PE)
Alan Cox33f0f882006-01-09 20:54:13 -0800715 flag = TTY_PARITY;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800716
717 else if (status & RCSR_FE)
Alan Cox33f0f882006-01-09 20:54:13 -0800718 flag = TTY_FRAME;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800719
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720 else if (status & RCSR_OE)
Alan Cox33f0f882006-01-09 20:54:13 -0800721 flag = TTY_OVERRUN;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800722
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 else
Alan Cox33f0f882006-01-09 20:54:13 -0800724 flag = TTY_NORMAL;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800725
Alan Coxa72492b2008-07-22 11:17:05 +0100726 if (tty_insert_flip_char(tty, ch, flag))
Alan Cox33f0f882006-01-09 20:54:13 -0800727 tty_flip_buffer_push(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728 func_exit();
729}
730
731
Alan Coxa72492b2008-07-22 11:17:05 +0100732static inline void sx_receive(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733{
734 struct specialix_port *port;
735 struct tty_struct *tty;
736 unsigned char count;
737
738 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -0800739
Alan Coxa72492b2008-07-22 11:17:05 +0100740 port = sx_get_port(bp, "Receive");
741 if (port == NULL) {
742 dprintk(SX_DEBUG_RX, "Hmm, couldn't find port.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743 func_exit();
744 return;
745 }
Alan Cox44b7d1b2008-07-16 21:57:18 +0100746 tty = port->port.tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800747
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748 count = sx_in(bp, CD186x_RDCR);
Alan Coxa72492b2008-07-22 11:17:05 +0100749 dprintk(SX_DEBUG_RX, "port: %p: count: %d\n", port, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750 port->hits[count > 8 ? 9 : count]++;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800751
Alan Cox33f0f882006-01-09 20:54:13 -0800752 tty_buffer_request_room(tty, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753
Alan Cox33f0f882006-01-09 20:54:13 -0800754 while (count--)
755 tty_insert_flip_char(tty, sx_in(bp, CD186x_RDR), TTY_NORMAL);
756 tty_flip_buffer_push(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757 func_exit();
758}
759
760
Alan Coxa72492b2008-07-22 11:17:05 +0100761static inline void sx_transmit(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762{
763 struct specialix_port *port;
764 struct tty_struct *tty;
765 unsigned char count;
766
767 func_enter();
Alan Coxa72492b2008-07-22 11:17:05 +0100768 port = sx_get_port(bp, "Transmit");
769 if (port == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770 func_exit();
771 return;
772 }
Alan Coxa72492b2008-07-22 11:17:05 +0100773 dprintk(SX_DEBUG_TX, "port: %p\n", port);
Alan Cox44b7d1b2008-07-16 21:57:18 +0100774 tty = port->port.tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800775
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776 if (port->IER & IER_TXEMPTY) {
777 /* FIFO drained */
778 sx_out(bp, CD186x_CAR, port_No(port));
779 port->IER &= ~IER_TXEMPTY;
780 sx_out(bp, CD186x_IER, port->IER);
781 func_exit();
782 return;
783 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800784
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 if ((port->xmit_cnt <= 0 && !port->break_length)
786 || tty->stopped || tty->hw_stopped) {
787 sx_out(bp, CD186x_CAR, port_No(port));
788 port->IER &= ~IER_TXRDY;
789 sx_out(bp, CD186x_IER, port->IER);
790 func_exit();
791 return;
792 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800793
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 if (port->break_length) {
795 if (port->break_length > 0) {
796 if (port->COR2 & COR2_ETC) {
797 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
798 sx_out(bp, CD186x_TDR, CD186x_C_SBRK);
799 port->COR2 &= ~COR2_ETC;
800 }
801 count = min_t(int, port->break_length, 0xff);
802 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
803 sx_out(bp, CD186x_TDR, CD186x_C_DELAY);
804 sx_out(bp, CD186x_TDR, count);
Alan Coxa72492b2008-07-22 11:17:05 +0100805 port->break_length -= count;
806 if (port->break_length == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807 port->break_length--;
808 } else {
809 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
810 sx_out(bp, CD186x_TDR, CD186x_C_EBRK);
811 sx_out(bp, CD186x_COR2, port->COR2);
812 sx_wait_CCR(bp);
813 sx_out(bp, CD186x_CCR, CCR_CORCHG2);
814 port->break_length = 0;
815 }
816
817 func_exit();
818 return;
819 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800820
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821 count = CD186x_NFIFO;
822 do {
823 sx_out(bp, CD186x_TDR, port->xmit_buf[port->xmit_tail++]);
824 port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1);
825 if (--port->xmit_cnt <= 0)
826 break;
827 } while (--count > 0);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800828
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829 if (port->xmit_cnt <= 0) {
830 sx_out(bp, CD186x_CAR, port_No(port));
831 port->IER &= ~IER_TXRDY;
832 sx_out(bp, CD186x_IER, port->IER);
833 }
834 if (port->xmit_cnt <= port->wakeup_chars)
Alan Coxa72492b2008-07-22 11:17:05 +0100835 tty_wakeup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836
837 func_exit();
838}
839
840
Alan Coxa72492b2008-07-22 11:17:05 +0100841static inline void sx_check_modem(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842{
843 struct specialix_port *port;
844 struct tty_struct *tty;
845 unsigned char mcr;
846 int msvr_cd;
847
Alan Coxa72492b2008-07-22 11:17:05 +0100848 dprintk(SX_DEBUG_SIGNALS, "Modem intr. ");
849 port = sx_get_port(bp, "Modem");
850 if (port == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800852
Alan Cox44b7d1b2008-07-16 21:57:18 +0100853 tty = port->port.tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800854
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855 mcr = sx_in(bp, CD186x_MCR);
Alan Coxa72492b2008-07-22 11:17:05 +0100856 printk("mcr = %02x.\n", mcr); /* FIXME */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857
858 if ((mcr & MCR_CDCHG)) {
Alan Coxa72492b2008-07-22 11:17:05 +0100859 dprintk(SX_DEBUG_SIGNALS, "CD just changed... ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860 msvr_cd = sx_in(bp, CD186x_MSVR) & MSVR_CD;
861 if (msvr_cd) {
Alan Coxa72492b2008-07-22 11:17:05 +0100862 dprintk(SX_DEBUG_SIGNALS, "Waking up guys in open.\n");
Alan Cox44b7d1b2008-07-16 21:57:18 +0100863 wake_up_interruptible(&port->port.open_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864 } else {
Alan Coxa72492b2008-07-22 11:17:05 +0100865 dprintk(SX_DEBUG_SIGNALS, "Sending HUP.\n");
Jiri Slabyd0d4e1c2008-02-07 00:16:39 -0800866 tty_hangup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867 }
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)
Jiri Slabyd0d4e1c2008-02-07 00:16:39 -0800876 tty_wakeup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877 } 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)
Jiri Slabyd0d4e1c2008-02-07 00:16:39 -0800888 tty_wakeup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889 } 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 */
Jeff Garzika6f97b22007-10-31 05:20:49 -0400903static irqreturn_t sx_interrupt(int dummy, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904{
905 unsigned char status;
906 unsigned char ack;
Jeff Garzika6f97b22007-10-31 05:20:49 -0400907 struct specialix_board *bp = dev_id;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908 unsigned long loop = 0;
909 int saved_reg;
910 unsigned long flags;
911
912 func_enter();
913
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914 spin_lock_irqsave(&bp->lock, flags);
915
Alan Coxa72492b2008-07-22 11:17:05 +0100916 dprintk(SX_DEBUG_FLOW, "enter %s port %d room: %ld\n", __func__,
917 port_No(sx_get_port(bp, "INT")),
918 SERIAL_XMIT_SIZE - sx_get_port(bp, "ITN")->xmit_cnt - 1);
Jeff Garzikc7bec5a2006-10-06 15:00:58 -0400919 if (!(bp->flags & SX_BOARD_ACTIVE)) {
Alan Coxa72492b2008-07-22 11:17:05 +0100920 dprintk(SX_DEBUG_IRQ, "sx: False interrupt. irq %d.\n",
921 bp->irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922 spin_unlock_irqrestore(&bp->lock, flags);
923 func_exit();
924 return IRQ_NONE;
925 }
926
927 saved_reg = bp->reg;
928
Alan Coxa72492b2008-07-22 11:17:05 +0100929 while (++loop < 16) {
930 status = sx_in(bp, CD186x_SRSR) &
931 (SRSR_RREQint | SRSR_TREQint | SRSR_MREQint);
932 if (status == 0)
933 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934 if (status & SRSR_RREQint) {
935 ack = sx_in(bp, CD186x_RRAR);
936
937 if (ack == (SX_ID | GIVR_IT_RCV))
938 sx_receive(bp);
939 else if (ack == (SX_ID | GIVR_IT_REXC))
940 sx_receive_exc(bp);
941 else
Alan Coxa72492b2008-07-22 11:17:05 +0100942 printk(KERN_ERR
943 "sx%d: status: 0x%x Bad receive ack 0x%02x.\n",
944 board_No(bp), status, ack);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800945
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946 } else if (status & SRSR_TREQint) {
947 ack = sx_in(bp, CD186x_TRAR);
948
949 if (ack == (SX_ID | GIVR_IT_TX))
950 sx_transmit(bp);
951 else
952 printk(KERN_ERR "sx%d: status: 0x%x Bad transmit ack 0x%02x. port: %d\n",
Alan Coxa72492b2008-07-22 11:17:05 +0100953 board_No(bp), status, ack,
954 port_No(sx_get_port(bp, "Int")));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955 } else if (status & SRSR_MREQint) {
956 ack = sx_in(bp, CD186x_MRAR);
957
Jeff Garzikd61780c2005-10-30 15:01:51 -0800958 if (ack == (SX_ID | GIVR_IT_MODEM))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959 sx_check_modem(bp);
960 else
Alan Coxa72492b2008-07-22 11:17:05 +0100961 printk(KERN_ERR
962 "sx%d: status: 0x%x Bad modem ack 0x%02x.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700963 board_No(bp), status, ack);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800964
965 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966
967 sx_out(bp, CD186x_EOIR, 0); /* Mark end of interrupt */
968 }
969 bp->reg = saved_reg;
Alan Coxa72492b2008-07-22 11:17:05 +0100970 outb(bp->reg, bp->base + SX_ADDR_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971 spin_unlock_irqrestore(&bp->lock, flags);
972 func_exit();
973 return IRQ_HANDLED;
974}
975
976
977/*
978 * Routines for open & close processing.
979 */
980
Alan Coxa72492b2008-07-22 11:17:05 +0100981static void turn_ints_off(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982{
983 unsigned long flags;
984
985 func_enter();
986 if (bp->flags & SX_BOARD_IS_PCI) {
987 /* This was intended for enabeling the interrupt on the
988 * PCI card. However it seems that it's already enabled
989 * and as PCI interrupts can be shared, there is no real
990 * reason to have to turn it off. */
991 }
992
993 spin_lock_irqsave(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +0100994 (void) sx_in_off(bp, 0); /* Turn off interrupts. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995 spin_unlock_irqrestore(&bp->lock, flags);
996
997 func_exit();
998}
999
Alan Coxa72492b2008-07-22 11:17:05 +01001000static void turn_ints_on(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001{
1002 unsigned long flags;
1003
1004 func_enter();
1005
1006 if (bp->flags & SX_BOARD_IS_PCI) {
1007 /* play with the PCI chip. See comment above. */
1008 }
1009 spin_lock_irqsave(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001010 (void) sx_in(bp, 0); /* Turn ON interrupts. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011 spin_unlock_irqrestore(&bp->lock, flags);
1012
1013 func_exit();
1014}
1015
1016
1017/* Called with disabled interrupts */
Alan Coxa72492b2008-07-22 11:17:05 +01001018static inline int sx_setup_board(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019{
1020 int error;
1021
Jeff Garzikd61780c2005-10-30 15:01:51 -08001022 if (bp->flags & SX_BOARD_ACTIVE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001023 return 0;
1024
1025 if (bp->flags & SX_BOARD_IS_PCI)
Alan Coxa72492b2008-07-22 11:17:05 +01001026 error = request_irq(bp->irq, sx_interrupt,
1027 IRQF_DISABLED | IRQF_SHARED, "specialix IO8+", bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028 else
Alan Coxa72492b2008-07-22 11:17:05 +01001029 error = request_irq(bp->irq, sx_interrupt,
1030 IRQF_DISABLED, "specialix IO8+", bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031
Jeff Garzikd61780c2005-10-30 15:01:51 -08001032 if (error)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033 return error;
1034
Alan Coxa72492b2008-07-22 11:17:05 +01001035 turn_ints_on(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036 bp->flags |= SX_BOARD_ACTIVE;
1037
1038 return 0;
1039}
1040
1041
1042/* Called with disabled interrupts */
1043static inline void sx_shutdown_board(struct specialix_board *bp)
1044{
1045 func_enter();
1046
1047 if (!(bp->flags & SX_BOARD_ACTIVE)) {
1048 func_exit();
1049 return;
1050 }
1051
1052 bp->flags &= ~SX_BOARD_ACTIVE;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001053
Alan Coxa72492b2008-07-22 11:17:05 +01001054 dprintk(SX_DEBUG_IRQ, "Freeing IRQ%d for board %d.\n",
1055 bp->irq, board_No(bp));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056 free_irq(bp->irq, bp);
Alan Coxa72492b2008-07-22 11:17:05 +01001057 turn_ints_off(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058 func_exit();
1059}
1060
1061
1062/*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001063 * Setting up port characteristics.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064 * Must be called with disabled interrupts
1065 */
Alan Coxa72492b2008-07-22 11:17:05 +01001066static void sx_change_speed(struct specialix_board *bp,
1067 struct specialix_port *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068{
1069 struct tty_struct *tty;
1070 unsigned long baud;
1071 long tmp;
1072 unsigned char cor1 = 0, cor3 = 0;
1073 unsigned char mcor1 = 0, mcor2 = 0;
1074 static unsigned long again;
1075 unsigned long flags;
1076
1077 func_enter();
1078
Alan Coxa72492b2008-07-22 11:17:05 +01001079 tty = port->port.tty;
1080 if (!tty || !tty->termios) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 func_exit();
1082 return;
1083 }
1084
1085 port->IER = 0;
1086 port->COR2 = 0;
1087 /* Select port on the board */
1088 spin_lock_irqsave(&bp->lock, flags);
1089 sx_out(bp, CD186x_CAR, port_No(port));
1090
1091 /* The Specialix board doens't implement the RTS lines.
1092 They are used to set the IRQ level. Don't touch them. */
1093 if (SX_CRTSCTS(tty))
1094 port->MSVR = MSVR_DTR | (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
1095 else
1096 port->MSVR = (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
1097 spin_unlock_irqrestore(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001098 dprintk(SX_DEBUG_TERMIOS, "sx: got MSVR=%02x.\n", port->MSVR);
Alan Cox67cc0162006-09-29 02:01:39 -07001099 baud = tty_get_baud_rate(tty);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001100
Alan Cox67cc0162006-09-29 02:01:39 -07001101 if (baud == 38400) {
Alan Cox44b7d1b2008-07-16 21:57:18 +01001102 if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
Adrian Bunka4bb2cf2006-10-17 00:10:00 -07001103 baud = 57600;
Alan Cox44b7d1b2008-07-16 21:57:18 +01001104 if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
Adrian Bunka4bb2cf2006-10-17 00:10:00 -07001105 baud = 115200;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001107
Alan Cox67cc0162006-09-29 02:01:39 -07001108 if (!baud) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001109 /* Drop DTR & exit */
Alan Coxa72492b2008-07-22 11:17:05 +01001110 dprintk(SX_DEBUG_TERMIOS, "Dropping DTR... Hmm....\n");
1111 if (!SX_CRTSCTS(tty)) {
1112 port->MSVR &= ~MSVR_DTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113 spin_lock_irqsave(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001114 sx_out(bp, CD186x_MSVR, port->MSVR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001115 spin_unlock_irqrestore(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001116 } else
1117 dprintk(SX_DEBUG_TERMIOS, "Can't drop DTR: no DTR.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118 return;
1119 } else {
1120 /* Set DTR on */
Alan Coxa72492b2008-07-22 11:17:05 +01001121 if (!SX_CRTSCTS(tty))
1122 port->MSVR |= MSVR_DTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001123 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001124
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125 /*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001126 * Now we must calculate some speed depended things
Linus Torvalds1da177e2005-04-16 15:20:36 -07001127 */
1128
1129 /* Set baud rate for port */
1130 tmp = port->custom_divisor ;
Alan Coxa72492b2008-07-22 11:17:05 +01001131 if (tmp)
1132 printk(KERN_INFO
1133 "sx%d: Using custom baud rate divisor %ld. \n"
1134 "This is an untested option, please be careful.\n",
1135 port_No(port), tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136 else
Alan Coxa72492b2008-07-22 11:17:05 +01001137 tmp = (((SX_OSCFREQ + baud/2) / baud + CD186x_TPC/2) /
1138 CD186x_TPC);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139
Alan Coxa72492b2008-07-22 11:17:05 +01001140 if (tmp < 0x10 && time_before(again, jiffies)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141 again = jiffies + HZ * 60;
1142 /* Page 48 of version 2.0 of the CL-CD1865 databook */
1143 if (tmp >= 12) {
Alan Coxa72492b2008-07-22 11:17:05 +01001144 printk(KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
1145 "Performance degradation is possible.\n"
1146 "Read specialix.txt for more info.\n",
1147 port_No(port), tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001148 } else {
Alan Coxa72492b2008-07-22 11:17:05 +01001149 printk(KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
1150 "Warning: overstressing Cirrus chip. This might not work.\n"
1151 "Read specialix.txt for more info.\n", port_No(port), tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152 }
1153 }
1154 spin_lock_irqsave(&bp->lock, flags);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001155 sx_out(bp, CD186x_RBPRH, (tmp >> 8) & 0xff);
1156 sx_out(bp, CD186x_TBPRH, (tmp >> 8) & 0xff);
1157 sx_out(bp, CD186x_RBPRL, tmp & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158 sx_out(bp, CD186x_TBPRL, tmp & 0xff);
1159 spin_unlock_irqrestore(&bp->lock, flags);
Adrian Bunka4bb2cf2006-10-17 00:10:00 -07001160 if (port->custom_divisor)
Alan Coxa72492b2008-07-22 11:17:05 +01001161 baud = (SX_OSCFREQ + port->custom_divisor/2) /
1162 port->custom_divisor;
Adrian Bunka4bb2cf2006-10-17 00:10:00 -07001163 baud = (baud + 5) / 10; /* Estimated CPS */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164
1165 /* Two timer ticks seems enough to wakeup something like SLIP driver */
Jeff Garzikd61780c2005-10-30 15:01:51 -08001166 tmp = ((baud + HZ/2) / HZ) * 2 - CD186x_NFIFO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167 port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ?
1168 SERIAL_XMIT_SIZE - 1 : tmp);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001169
Linus Torvalds1da177e2005-04-16 15:20:36 -07001170 /* Receiver timeout will be transmission time for 1.5 chars */
1171 tmp = (SPECIALIX_TPS + SPECIALIX_TPS/2 + baud/2) / baud;
1172 tmp = (tmp > 0xff) ? 0xff : tmp;
1173 spin_lock_irqsave(&bp->lock, flags);
1174 sx_out(bp, CD186x_RTPR, tmp);
1175 spin_unlock_irqrestore(&bp->lock, flags);
1176 switch (C_CSIZE(tty)) {
Alan Coxa72492b2008-07-22 11:17:05 +01001177 case CS5:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001178 cor1 |= COR1_5BITS;
1179 break;
Alan Coxa72492b2008-07-22 11:17:05 +01001180 case CS6:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001181 cor1 |= COR1_6BITS;
1182 break;
Alan Coxa72492b2008-07-22 11:17:05 +01001183 case CS7:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001184 cor1 |= COR1_7BITS;
1185 break;
Alan Coxa72492b2008-07-22 11:17:05 +01001186 case CS8:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187 cor1 |= COR1_8BITS;
1188 break;
1189 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001190
1191 if (C_CSTOPB(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001192 cor1 |= COR1_2SB;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001193
Linus Torvalds1da177e2005-04-16 15:20:36 -07001194 cor1 |= COR1_IGNORE;
1195 if (C_PARENB(tty)) {
1196 cor1 |= COR1_NORMPAR;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001197 if (C_PARODD(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198 cor1 |= COR1_ODDP;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001199 if (I_INPCK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001200 cor1 &= ~COR1_IGNORE;
1201 }
1202 /* Set marking of some errors */
1203 port->mark_mask = RCSR_OE | RCSR_TOUT;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001204 if (I_INPCK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001205 port->mark_mask |= RCSR_FE | RCSR_PE;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001206 if (I_BRKINT(tty) || I_PARMRK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207 port->mark_mask |= RCSR_BREAK;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001208 if (I_IGNPAR(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209 port->mark_mask &= ~(RCSR_FE | RCSR_PE);
1210 if (I_IGNBRK(tty)) {
1211 port->mark_mask &= ~RCSR_BREAK;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001212 if (I_IGNPAR(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213 /* Real raw mode. Ignore all */
1214 port->mark_mask &= ~RCSR_OE;
1215 }
1216 /* Enable Hardware Flow Control */
1217 if (C_CRTSCTS(tty)) {
1218#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
1219 port->IER |= IER_DSR | IER_CTS;
1220 mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD;
1221 mcor2 |= MCOR2_DSROD | MCOR2_CTSOD;
1222 spin_lock_irqsave(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001223 tty->hw_stopped = !(sx_in(bp, CD186x_MSVR) &
1224 (MSVR_CTS|MSVR_DSR));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225 spin_unlock_irqrestore(&bp->lock, flags);
1226#else
Jeff Garzikd61780c2005-10-30 15:01:51 -08001227 port->COR2 |= COR2_CTSAE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001228#endif
1229 }
1230 /* Enable Software Flow Control. FIXME: I'm not sure about this */
1231 /* Some people reported that it works, but I still doubt it */
1232 if (I_IXON(tty)) {
1233 port->COR2 |= COR2_TXIBE;
1234 cor3 |= (COR3_FCT | COR3_SCDE);
1235 if (I_IXANY(tty))
1236 port->COR2 |= COR2_IXM;
1237 spin_lock_irqsave(&bp->lock, flags);
1238 sx_out(bp, CD186x_SCHR1, START_CHAR(tty));
1239 sx_out(bp, CD186x_SCHR2, STOP_CHAR(tty));
1240 sx_out(bp, CD186x_SCHR3, START_CHAR(tty));
1241 sx_out(bp, CD186x_SCHR4, STOP_CHAR(tty));
1242 spin_unlock_irqrestore(&bp->lock, flags);
1243 }
1244 if (!C_CLOCAL(tty)) {
1245 /* Enable CD check */
1246 port->IER |= IER_CD;
1247 mcor1 |= MCOR1_CDZD;
1248 mcor2 |= MCOR2_CDOD;
1249 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001250
1251 if (C_CREAD(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001252 /* Enable receiver */
1253 port->IER |= IER_RXD;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001254
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255 /* Set input FIFO size (1-8 bytes) */
1256 cor3 |= sx_rxfifo;
1257 /* Setting up CD186x channel registers */
1258 spin_lock_irqsave(&bp->lock, flags);
1259 sx_out(bp, CD186x_COR1, cor1);
1260 sx_out(bp, CD186x_COR2, port->COR2);
1261 sx_out(bp, CD186x_COR3, cor3);
1262 spin_unlock_irqrestore(&bp->lock, flags);
1263 /* Make CD186x know about registers change */
1264 sx_wait_CCR(bp);
1265 spin_lock_irqsave(&bp->lock, flags);
1266 sx_out(bp, CD186x_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3);
1267 /* Setting up modem option registers */
Alan Coxa72492b2008-07-22 11:17:05 +01001268 dprintk(SX_DEBUG_TERMIOS, "Mcor1 = %02x, mcor2 = %02x.\n",
1269 mcor1, mcor2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270 sx_out(bp, CD186x_MCOR1, mcor1);
1271 sx_out(bp, CD186x_MCOR2, mcor2);
1272 spin_unlock_irqrestore(&bp->lock, flags);
1273 /* Enable CD186x transmitter & receiver */
1274 sx_wait_CCR(bp);
1275 spin_lock_irqsave(&bp->lock, flags);
1276 sx_out(bp, CD186x_CCR, CCR_TXEN | CCR_RXEN);
1277 /* Enable interrupts */
1278 sx_out(bp, CD186x_IER, port->IER);
1279 /* And finally set the modem lines... */
1280 sx_out(bp, CD186x_MSVR, port->MSVR);
1281 spin_unlock_irqrestore(&bp->lock, flags);
1282
1283 func_exit();
1284}
1285
1286
1287/* Must be called with interrupts enabled */
Alan Coxa72492b2008-07-22 11:17:05 +01001288static int sx_setup_port(struct specialix_board *bp,
1289 struct specialix_port *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290{
1291 unsigned long flags;
1292
1293 func_enter();
1294
Alan Cox44b7d1b2008-07-16 21:57:18 +01001295 if (port->port.flags & ASYNC_INITIALIZED) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296 func_exit();
1297 return 0;
1298 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001299
Linus Torvalds1da177e2005-04-16 15:20:36 -07001300 if (!port->xmit_buf) {
1301 /* We may sleep in get_zeroed_page() */
1302 unsigned long tmp;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001303
Alan Coxa72492b2008-07-22 11:17:05 +01001304 tmp = get_zeroed_page(GFP_KERNEL);
1305 if (tmp == 0L) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001306 func_exit();
1307 return -ENOMEM;
1308 }
1309
1310 if (port->xmit_buf) {
1311 free_page(tmp);
1312 func_exit();
1313 return -ERESTARTSYS;
1314 }
1315 port->xmit_buf = (unsigned char *) tmp;
1316 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001317
Linus Torvalds1da177e2005-04-16 15:20:36 -07001318 spin_lock_irqsave(&port->lock, flags);
1319
Alan Cox44b7d1b2008-07-16 21:57:18 +01001320 if (port->port.tty)
1321 clear_bit(TTY_IO_ERROR, &port->port.tty->flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322
1323 port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
1324 sx_change_speed(bp, port);
Alan Cox44b7d1b2008-07-16 21:57:18 +01001325 port->port.flags |= ASYNC_INITIALIZED;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001326
1327 spin_unlock_irqrestore(&port->lock, flags);
1328
Jeff Garzikd61780c2005-10-30 15:01:51 -08001329
Linus Torvalds1da177e2005-04-16 15:20:36 -07001330 func_exit();
1331 return 0;
1332}
1333
1334
1335/* Must be called with interrupts disabled */
Alan Coxa72492b2008-07-22 11:17:05 +01001336static void sx_shutdown_port(struct specialix_board *bp,
1337 struct specialix_port *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001338{
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
Alan Cox44b7d1b2008-07-16 21:57:18 +01001345 if (!(port->port.flags & ASYNC_INITIALIZED)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346 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) {
Alan Coxa72492b2008-07-22 11:17:05 +01001351 dprintk(SX_DEBUG_FIFO,
1352 "sx%d: port %d: %ld overruns, FIFO hits [ ",
1353 board_No(bp), port_No(port), port->overrun);
1354 for (i = 0; i < 10; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355 dprintk(SX_DEBUG_FIFO, "%ld ", port->hits[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001356 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
Alan Coxa72492b2008-07-22 11:17:05 +01001368 tty = port->port.tty;
1369 if (tty == NULL || C_HUPCL(tty)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370 /* Drop DTR */
1371 sx_out(bp, CD186x_MSVDTR, 0);
1372 }
1373 spin_unlock_irqrestore(&bp->lock, flags);
1374 /* Reset port */
1375 sx_wait_CCR(bp);
1376 spin_lock_irqsave(&bp->lock, flags);
1377 sx_out(bp, CD186x_CCR, CCR_SOFTRESET);
1378 /* Disable all interrupts from this port */
1379 port->IER = 0;
1380 sx_out(bp, CD186x_IER, port->IER);
1381 spin_unlock_irqrestore(&bp->lock, flags);
1382 if (tty)
1383 set_bit(TTY_IO_ERROR, &tty->flags);
Alan Cox44b7d1b2008-07-16 21:57:18 +01001384 port->port.flags &= ~ASYNC_INITIALIZED;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001385
1386 if (!bp->count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001387 sx_shutdown_board(bp);
1388 func_exit();
1389}
1390
Jeff Garzikd61780c2005-10-30 15:01:51 -08001391
Alan Coxa72492b2008-07-22 11:17:05 +01001392static int block_til_ready(struct tty_struct *tty, struct file *filp,
1393 struct specialix_port *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001394{
1395 DECLARE_WAITQUEUE(wait, current);
1396 struct specialix_board *bp = port_Board(port);
1397 int retval;
1398 int do_clocal = 0;
1399 int CD;
1400 unsigned long flags;
1401
1402 func_enter();
1403
1404 /*
1405 * If the device is in the middle of being closed, then block
1406 * until it's done, and then try again.
1407 */
Alan Cox44b7d1b2008-07-16 21:57:18 +01001408 if (tty_hung_up_p(filp) || port->port.flags & ASYNC_CLOSING) {
1409 interruptible_sleep_on(&port->port.close_wait);
1410 if (port->port.flags & ASYNC_HUP_NOTIFY) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001411 func_exit();
1412 return -EAGAIN;
1413 } else {
1414 func_exit();
1415 return -ERESTARTSYS;
1416 }
1417 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001418
Linus Torvalds1da177e2005-04-16 15:20:36 -07001419 /*
1420 * If non-blocking mode is set, or the port is not enabled,
1421 * then make the check up front and then exit.
1422 */
1423 if ((filp->f_flags & O_NONBLOCK) ||
1424 (tty->flags & (1 << TTY_IO_ERROR))) {
Alan Cox44b7d1b2008-07-16 21:57:18 +01001425 port->port.flags |= ASYNC_NORMAL_ACTIVE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001426 func_exit();
1427 return 0;
1428 }
1429
1430 if (C_CLOCAL(tty))
1431 do_clocal = 1;
1432
1433 /*
1434 * Block waiting for the carrier detect and the line to become
1435 * free (i.e., not in use by the callout). While we are in
1436 * this loop, info->count is dropped by one, so that
1437 * rs_close() knows when to free things. We restore it upon
1438 * exit, either normal or abnormal.
1439 */
1440 retval = 0;
Alan Cox44b7d1b2008-07-16 21:57:18 +01001441 add_wait_queue(&port->port.open_wait, &wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442 spin_lock_irqsave(&port->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001443 if (!tty_hung_up_p(filp))
Alan Cox44b7d1b2008-07-16 21:57:18 +01001444 port->port.count--;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001445 spin_unlock_irqrestore(&port->lock, flags);
Alan Cox44b7d1b2008-07-16 21:57:18 +01001446 port->port.blocked_open++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447 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;
Alan Coxa72492b2008-07-22 11:17:05 +01001451 if (SX_CRTSCTS(tty)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452 /* Activate RTS */
1453 port->MSVR |= MSVR_DTR; /* WTF? */
Alan Coxa72492b2008-07-22 11:17:05 +01001454 sx_out(bp, CD186x_MSVR, port->MSVR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001455 } else {
1456 /* Activate DTR */
1457 port->MSVR |= MSVR_DTR;
Alan Coxa72492b2008-07-22 11:17:05 +01001458 sx_out(bp, CD186x_MSVR, port->MSVR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001459 }
1460 spin_unlock_irqrestore(&bp->lock, flags);
1461 set_current_state(TASK_INTERRUPTIBLE);
1462 if (tty_hung_up_p(filp) ||
Alan Cox44b7d1b2008-07-16 21:57:18 +01001463 !(port->port.flags & ASYNC_INITIALIZED)) {
1464 if (port->port.flags & ASYNC_HUP_NOTIFY)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001465 retval = -EAGAIN;
1466 else
Jeff Garzikd61780c2005-10-30 15:01:51 -08001467 retval = -ERESTARTSYS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001468 break;
1469 }
Alan Cox44b7d1b2008-07-16 21:57:18 +01001470 if (!(port->port.flags & ASYNC_CLOSING) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001471 (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);
Alan Cox44b7d1b2008-07-16 21:57:18 +01001481 remove_wait_queue(&port->port.open_wait, &wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001482 spin_lock_irqsave(&port->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001483 if (!tty_hung_up_p(filp))
Alan Cox44b7d1b2008-07-16 21:57:18 +01001484 port->port.count++;
Alan Cox44b7d1b2008-07-16 21:57:18 +01001485 port->port.blocked_open--;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001486 spin_unlock_irqrestore(&port->lock, flags);
1487 if (retval) {
1488 func_exit();
1489 return retval;
1490 }
1491
Alan Cox44b7d1b2008-07-16 21:57:18 +01001492 port->port.flags |= ASYNC_NORMAL_ACTIVE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001493 func_exit();
1494 return 0;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001495}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001496
1497
Alan Coxa72492b2008-07-22 11:17:05 +01001498static int sx_open(struct tty_struct *tty, struct file *filp)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499{
1500 int board;
1501 int error;
Alan Coxa72492b2008-07-22 11:17:05 +01001502 struct specialix_port *port;
1503 struct specialix_board *bp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001504 int i;
1505 unsigned long flags;
1506
1507 func_enter();
1508
1509 board = SX_BOARD(tty->index);
1510
1511 if (board >= SX_NBOARD || !(sx_board[board].flags & SX_BOARD_PRESENT)) {
1512 func_exit();
1513 return -ENODEV;
1514 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001515
Linus Torvalds1da177e2005-04-16 15:20:36 -07001516 bp = &sx_board[board];
1517 port = sx_port + board * SX_NPORT + SX_PORT(tty->index);
1518 port->overrun = 0;
1519 for (i = 0; i < 10; i++)
Alan Coxa72492b2008-07-22 11:17:05 +01001520 port->hits[i] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001521
Alan Coxa72492b2008-07-22 11:17:05 +01001522 dprintk(SX_DEBUG_OPEN,
1523 "Board = %d, bp = %p, port = %p, portno = %d.\n",
1524 board, bp, port, SX_PORT(tty->index));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001525
1526 if (sx_paranoia_check(port, tty->name, "sx_open")) {
1527 func_enter();
1528 return -ENODEV;
1529 }
1530
Alan Coxa72492b2008-07-22 11:17:05 +01001531 error = sx_setup_board(bp);
1532 if (error) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533 func_exit();
1534 return error;
1535 }
1536
1537 spin_lock_irqsave(&bp->lock, flags);
Alan Cox44b7d1b2008-07-16 21:57:18 +01001538 port->port.count++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001539 bp->count++;
1540 tty->driver_data = port;
Alan Cox44b7d1b2008-07-16 21:57:18 +01001541 port->port.tty = tty;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542 spin_unlock_irqrestore(&bp->lock, flags);
1543
Alan Coxa72492b2008-07-22 11:17:05 +01001544 error = sx_setup_port(bp, port);
1545 if (error) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001546 func_enter();
1547 return error;
1548 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001549
Alan Coxa72492b2008-07-22 11:17:05 +01001550 error = block_til_ready(tty, filp, port);
1551 if (error) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001552 func_enter();
1553 return error;
1554 }
1555
1556 func_exit();
1557 return 0;
1558}
1559
Alan Cox978e5952008-04-30 00:53:59 -07001560static void sx_flush_buffer(struct tty_struct *tty)
1561{
1562 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1563 unsigned long flags;
Alan Coxa72492b2008-07-22 11:17:05 +01001564 struct specialix_board *bp;
Alan Cox978e5952008-04-30 00:53:59 -07001565
1566 func_enter();
1567
1568 if (sx_paranoia_check(port, tty->name, "sx_flush_buffer")) {
1569 func_exit();
1570 return;
1571 }
1572
1573 bp = port_Board(port);
1574 spin_lock_irqsave(&port->lock, flags);
1575 port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
1576 spin_unlock_irqrestore(&port->lock, flags);
1577 tty_wakeup(tty);
1578
1579 func_exit();
1580}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001581
Alan Coxa72492b2008-07-22 11:17:05 +01001582static void sx_close(struct tty_struct *tty, struct file *filp)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001583{
Alan Coxa72492b2008-07-22 11:17:05 +01001584 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001585 struct specialix_board *bp;
1586 unsigned long flags;
1587 unsigned long timeout;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001588
Linus Torvalds1da177e2005-04-16 15:20:36 -07001589 func_enter();
1590 if (!port || sx_paranoia_check(port, tty->name, "close")) {
1591 func_exit();
1592 return;
1593 }
1594 spin_lock_irqsave(&port->lock, flags);
1595
1596 if (tty_hung_up_p(filp)) {
1597 spin_unlock_irqrestore(&port->lock, flags);
1598 func_exit();
1599 return;
1600 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001601
Linus Torvalds1da177e2005-04-16 15:20:36 -07001602 bp = port_Board(port);
Alan Cox44b7d1b2008-07-16 21:57:18 +01001603 if ((tty->count == 1) && (port->port.count != 1)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001604 printk(KERN_ERR "sx%d: sx_close: bad port count;"
1605 " tty->count is 1, port count is %d\n",
Alan Cox44b7d1b2008-07-16 21:57:18 +01001606 board_No(bp), port->port.count);
1607 port->port.count = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001608 }
1609
Alan Cox44b7d1b2008-07-16 21:57:18 +01001610 if (port->port.count > 1) {
1611 port->port.count--;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001612 bp->count--;
1613
1614 spin_unlock_irqrestore(&port->lock, flags);
1615
1616 func_exit();
1617 return;
1618 }
Alan Cox44b7d1b2008-07-16 21:57:18 +01001619 port->port.flags |= ASYNC_CLOSING;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001620 /*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001621 * Now we wait for the transmit buffer to clear; and we notify
Linus Torvalds1da177e2005-04-16 15:20:36 -07001622 * the line discipline to only process XON/XOFF characters.
1623 */
1624 tty->closing = 1;
1625 spin_unlock_irqrestore(&port->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001626 dprintk(SX_DEBUG_OPEN, "Closing\n");
1627 if (port->port.closing_wait != ASYNC_CLOSING_WAIT_NONE)
Alan Cox44b7d1b2008-07-16 21:57:18 +01001628 tty_wait_until_sent(tty, port->port.closing_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001629 /*
1630 * At this point we stop accepting input. To do this, we
1631 * disable the receive line status interrupts, and tell the
1632 * interrupt driver to stop checking the data ready bit in the
1633 * line status register.
1634 */
Alan Coxa72492b2008-07-22 11:17:05 +01001635 dprintk(SX_DEBUG_OPEN, "Closed\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001636 port->IER &= ~IER_RXD;
Alan Cox44b7d1b2008-07-16 21:57:18 +01001637 if (port->port.flags & ASYNC_INITIALIZED) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001638 port->IER &= ~IER_TXRDY;
1639 port->IER |= IER_TXEMPTY;
1640 spin_lock_irqsave(&bp->lock, flags);
1641 sx_out(bp, CD186x_CAR, port_No(port));
1642 sx_out(bp, CD186x_IER, port->IER);
1643 spin_unlock_irqrestore(&bp->lock, flags);
1644 /*
1645 * Before we drop DTR, make sure the UART transmitter
1646 * has completely drained; this is especially
1647 * important if there is a transmit FIFO!
1648 */
1649 timeout = jiffies+HZ;
Alan Coxa72492b2008-07-22 11:17:05 +01001650 while (port->IER & IER_TXEMPTY) {
1651 set_current_state(TASK_INTERRUPTIBLE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001652 msleep_interruptible(jiffies_to_msecs(port->timeout));
1653 if (time_after(jiffies, timeout)) {
Alan Coxa72492b2008-07-22 11:17:05 +01001654 printk(KERN_INFO "Timeout waiting for close\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001655 break;
1656 }
1657 }
1658
1659 }
1660
1661 if (--bp->count < 0) {
Alan Coxa72492b2008-07-22 11:17:05 +01001662 printk(KERN_ERR
1663 "sx%d: sx_shutdown_port: bad board count: %d port: %d\n",
1664 board_No(bp), bp->count, tty->index);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001665 bp->count = 0;
1666 }
Alan Cox44b7d1b2008-07-16 21:57:18 +01001667 if (--port->port.count < 0) {
Alan Coxa72492b2008-07-22 11:17:05 +01001668 printk(KERN_ERR
1669 "sx%d: sx_close: bad port count for tty%d: %d\n",
1670 board_No(bp), port_No(port), port->port.count);
Alan Cox44b7d1b2008-07-16 21:57:18 +01001671 port->port.count = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001672 }
1673
1674 sx_shutdown_port(bp, port);
Alan Cox978e5952008-04-30 00:53:59 -07001675 sx_flush_buffer(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001676 tty_ldisc_flush(tty);
1677 spin_lock_irqsave(&port->lock, flags);
1678 tty->closing = 0;
Alan Cox44b7d1b2008-07-16 21:57:18 +01001679 port->port.tty = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001680 spin_unlock_irqrestore(&port->lock, flags);
Alan Cox44b7d1b2008-07-16 21:57:18 +01001681 if (port->port.blocked_open) {
Alan Coxa72492b2008-07-22 11:17:05 +01001682 if (port->port.close_delay)
1683 msleep_interruptible(
1684 jiffies_to_msecs(port->port.close_delay));
Alan Cox44b7d1b2008-07-16 21:57:18 +01001685 wake_up_interruptible(&port->port.open_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001686 }
Alan Cox44b7d1b2008-07-16 21:57:18 +01001687 port->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
1688 wake_up_interruptible(&port->port.close_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001689
1690 func_exit();
1691}
1692
1693
Alan Coxa72492b2008-07-22 11:17:05 +01001694static int sx_write(struct tty_struct *tty,
1695 const unsigned char *buf, int count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001696{
1697 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1698 struct specialix_board *bp;
1699 int c, total = 0;
1700 unsigned long flags;
1701
1702 func_enter();
1703 if (sx_paranoia_check(port, tty->name, "sx_write")) {
1704 func_exit();
1705 return 0;
1706 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001707
Linus Torvalds1da177e2005-04-16 15:20:36 -07001708 bp = port_Board(port);
1709
Jiri Slaby365e0222006-09-30 23:28:11 -07001710 if (!port->xmit_buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001711 func_exit();
1712 return 0;
1713 }
1714
1715 while (1) {
1716 spin_lock_irqsave(&port->lock, flags);
1717 c = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
1718 SERIAL_XMIT_SIZE - port->xmit_head));
1719 if (c <= 0) {
1720 spin_unlock_irqrestore(&port->lock, flags);
1721 break;
1722 }
1723 memcpy(port->xmit_buf + port->xmit_head, buf, c);
1724 port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
1725 port->xmit_cnt += c;
1726 spin_unlock_irqrestore(&port->lock, flags);
1727
1728 buf += c;
1729 count -= c;
1730 total += c;
1731 }
1732
1733 spin_lock_irqsave(&bp->lock, flags);
1734 if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
1735 !(port->IER & IER_TXRDY)) {
1736 port->IER |= IER_TXRDY;
1737 sx_out(bp, CD186x_CAR, port_No(port));
1738 sx_out(bp, CD186x_IER, port->IER);
1739 }
1740 spin_unlock_irqrestore(&bp->lock, flags);
1741 func_exit();
1742
1743 return total;
1744}
1745
1746
Alan Coxa72492b2008-07-22 11:17:05 +01001747static int sx_put_char(struct tty_struct *tty, unsigned char ch)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001748{
1749 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1750 unsigned long flags;
Alan Coxa72492b2008-07-22 11:17:05 +01001751 struct specialix_board *bp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001752
1753 func_enter();
1754
1755 if (sx_paranoia_check(port, tty->name, "sx_put_char")) {
1756 func_exit();
Alan Cox6ae04572008-04-30 00:54:07 -07001757 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001758 }
Alan Coxa72492b2008-07-22 11:17:05 +01001759 dprintk(SX_DEBUG_TX, "check tty: %p %p\n", tty, port->xmit_buf);
Eric Sesterhenn326f28e92006-06-25 05:48:48 -07001760 if (!port->xmit_buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001761 func_exit();
Alan Cox6ae04572008-04-30 00:54:07 -07001762 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001763 }
1764 bp = port_Board(port);
1765 spin_lock_irqsave(&port->lock, flags);
1766
Alan Coxa72492b2008-07-22 11:17:05 +01001767 dprintk(SX_DEBUG_TX, "xmit_cnt: %d xmit_buf: %p\n",
1768 port->xmit_cnt, port->xmit_buf);
1769 if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1 || !port->xmit_buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001770 spin_unlock_irqrestore(&port->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001771 dprintk(SX_DEBUG_TX, "Exit size\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001772 func_exit();
Alan Cox6ae04572008-04-30 00:54:07 -07001773 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001774 }
Alan Coxa72492b2008-07-22 11:17:05 +01001775 dprintk(SX_DEBUG_TX, "Handle xmit: %p %p\n", port, port->xmit_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001776 port->xmit_buf[port->xmit_head++] = ch;
1777 port->xmit_head &= SERIAL_XMIT_SIZE - 1;
1778 port->xmit_cnt++;
1779 spin_unlock_irqrestore(&port->lock, flags);
1780
1781 func_exit();
Alan Cox6ae04572008-04-30 00:54:07 -07001782 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001783}
1784
1785
Alan Coxa72492b2008-07-22 11:17:05 +01001786static void sx_flush_chars(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001787{
1788 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1789 unsigned long flags;
Alan Coxa72492b2008-07-22 11:17:05 +01001790 struct specialix_board *bp = port_Board(port);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001791
1792 func_enter();
1793
1794 if (sx_paranoia_check(port, tty->name, "sx_flush_chars")) {
1795 func_exit();
1796 return;
1797 }
1798 if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
1799 !port->xmit_buf) {
1800 func_exit();
1801 return;
1802 }
1803 spin_lock_irqsave(&bp->lock, flags);
1804 port->IER |= IER_TXRDY;
1805 sx_out(port_Board(port), CD186x_CAR, port_No(port));
1806 sx_out(port_Board(port), CD186x_IER, port->IER);
1807 spin_unlock_irqrestore(&bp->lock, flags);
1808
1809 func_exit();
1810}
1811
1812
Alan Coxa72492b2008-07-22 11:17:05 +01001813static int sx_write_room(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001814{
1815 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1816 int ret;
1817
1818 func_enter();
1819
1820 if (sx_paranoia_check(port, tty->name, "sx_write_room")) {
1821 func_exit();
1822 return 0;
1823 }
1824
1825 ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;
1826 if (ret < 0)
1827 ret = 0;
1828
1829 func_exit();
1830 return ret;
1831}
1832
1833
1834static int sx_chars_in_buffer(struct tty_struct *tty)
1835{
1836 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1837
1838 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08001839
Linus Torvalds1da177e2005-04-16 15:20:36 -07001840 if (sx_paranoia_check(port, tty->name, "sx_chars_in_buffer")) {
1841 func_exit();
1842 return 0;
1843 }
1844 func_exit();
1845 return port->xmit_cnt;
1846}
1847
Linus Torvalds1da177e2005-04-16 15:20:36 -07001848static int sx_tiocmget(struct tty_struct *tty, struct file *file)
1849{
1850 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
Alan Coxa72492b2008-07-22 11:17:05 +01001851 struct specialix_board *bp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001852 unsigned char status;
1853 unsigned int result;
1854 unsigned long flags;
1855
1856 func_enter();
1857
Harvey Harrisonbf9d8922008-04-30 00:55:10 -07001858 if (sx_paranoia_check(port, tty->name, __func__)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001859 func_exit();
1860 return -ENODEV;
1861 }
1862
1863 bp = port_Board(port);
Alan Coxa72492b2008-07-22 11:17:05 +01001864 spin_lock_irqsave(&bp->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001865 sx_out(bp, CD186x_CAR, port_No(port));
1866 status = sx_in(bp, CD186x_MSVR);
1867 spin_unlock_irqrestore(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001868 dprintk(SX_DEBUG_INIT, "Got msvr[%d] = %02x, car = %d.\n",
1869 port_No(port), status, sx_in(bp, CD186x_CAR));
1870 dprintk(SX_DEBUG_INIT, "sx_port = %p, port = %p\n", sx_port, port);
Alan Cox44b7d1b2008-07-16 21:57:18 +01001871 if (SX_CRTSCTS(port->port.tty)) {
Jeff Garzikd61780c2005-10-30 15:01:51 -08001872 result = /* (status & MSVR_RTS) ? */ TIOCM_DTR /* : 0) */
Alan Coxa72492b2008-07-22 11:17:05 +01001873 | ((status & MSVR_DTR) ? TIOCM_RTS : 0)
1874 | ((status & MSVR_CD) ? TIOCM_CAR : 0)
1875 |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */
1876 | ((status & MSVR_CTS) ? TIOCM_CTS : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001877 } else {
Jeff Garzikd61780c2005-10-30 15:01:51 -08001878 result = /* (status & MSVR_RTS) ? */ TIOCM_RTS /* : 0) */
Alan Coxa72492b2008-07-22 11:17:05 +01001879 | ((status & MSVR_DTR) ? TIOCM_DTR : 0)
1880 | ((status & MSVR_CD) ? TIOCM_CAR : 0)
1881 |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */
1882 | ((status & MSVR_CTS) ? TIOCM_CTS : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001883 }
1884
1885 func_exit();
1886
1887 return result;
1888}
1889
1890
1891static int sx_tiocmset(struct tty_struct *tty, struct file *file,
1892 unsigned int set, unsigned int clear)
1893{
1894 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1895 unsigned long flags;
1896 struct specialix_board *bp;
1897
1898 func_enter();
1899
Harvey Harrisonbf9d8922008-04-30 00:55:10 -07001900 if (sx_paranoia_check(port, tty->name, __func__)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001901 func_exit();
1902 return -ENODEV;
1903 }
1904
1905 bp = port_Board(port);
1906
1907 spin_lock_irqsave(&port->lock, flags);
1908 /* if (set & TIOCM_RTS)
1909 port->MSVR |= MSVR_RTS; */
1910 /* if (set & TIOCM_DTR)
1911 port->MSVR |= MSVR_DTR; */
1912
Alan Cox44b7d1b2008-07-16 21:57:18 +01001913 if (SX_CRTSCTS(port->port.tty)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001914 if (set & TIOCM_RTS)
1915 port->MSVR |= MSVR_DTR;
1916 } else {
1917 if (set & TIOCM_DTR)
1918 port->MSVR |= MSVR_DTR;
1919 }
1920
1921 /* if (clear & TIOCM_RTS)
1922 port->MSVR &= ~MSVR_RTS; */
1923 /* if (clear & TIOCM_DTR)
1924 port->MSVR &= ~MSVR_DTR; */
Alan Cox44b7d1b2008-07-16 21:57:18 +01001925 if (SX_CRTSCTS(port->port.tty)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001926 if (clear & TIOCM_RTS)
1927 port->MSVR &= ~MSVR_DTR;
1928 } else {
1929 if (clear & TIOCM_DTR)
1930 port->MSVR &= ~MSVR_DTR;
1931 }
1932 spin_lock_irqsave(&bp->lock, flags);
1933 sx_out(bp, CD186x_CAR, port_No(port));
1934 sx_out(bp, CD186x_MSVR, port->MSVR);
1935 spin_unlock_irqrestore(&bp->lock, flags);
1936 spin_unlock_irqrestore(&port->lock, flags);
1937 func_exit();
1938 return 0;
1939}
1940
1941
Alan Coxa72492b2008-07-22 11:17:05 +01001942static inline void sx_send_break(struct specialix_port *port,
1943 unsigned long length)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001944{
1945 struct specialix_board *bp = port_Board(port);
1946 unsigned long flags;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001947
Linus Torvalds1da177e2005-04-16 15:20:36 -07001948 func_enter();
1949
Alan Coxa72492b2008-07-22 11:17:05 +01001950 spin_lock_irqsave(&port->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001951 port->break_length = SPECIALIX_TPS / HZ * length;
1952 port->COR2 |= COR2_ETC;
1953 port->IER |= IER_TXRDY;
1954 spin_lock_irqsave(&bp->lock, flags);
1955 sx_out(bp, CD186x_CAR, port_No(port));
1956 sx_out(bp, CD186x_COR2, port->COR2);
1957 sx_out(bp, CD186x_IER, port->IER);
1958 spin_unlock_irqrestore(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001959 spin_unlock_irqrestore(&port->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001960 sx_wait_CCR(bp);
1961 spin_lock_irqsave(&bp->lock, flags);
1962 sx_out(bp, CD186x_CCR, CCR_CORCHG2);
1963 spin_unlock_irqrestore(&bp->lock, flags);
1964 sx_wait_CCR(bp);
1965
1966 func_exit();
1967}
1968
1969
Alan Coxa72492b2008-07-22 11:17:05 +01001970static inline int sx_set_serial_info(struct specialix_port *port,
1971 struct serial_struct __user *newinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001972{
1973 struct serial_struct tmp;
1974 struct specialix_board *bp = port_Board(port);
1975 int change_speed;
1976
1977 func_enter();
Alan Coxb190e172008-04-30 00:53:22 -07001978
Linus Torvalds1da177e2005-04-16 15:20:36 -07001979 if (copy_from_user(&tmp, newinfo, sizeof(tmp))) {
1980 func_enter();
1981 return -EFAULT;
1982 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001983
Alan Coxb190e172008-04-30 00:53:22 -07001984 lock_kernel();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001985
Alan Cox44b7d1b2008-07-16 21:57:18 +01001986 change_speed = ((port->port.flags & ASYNC_SPD_MASK) !=
Linus Torvalds1da177e2005-04-16 15:20:36 -07001987 (tmp.flags & ASYNC_SPD_MASK));
1988 change_speed |= (tmp.custom_divisor != port->custom_divisor);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001989
Linus Torvalds1da177e2005-04-16 15:20:36 -07001990 if (!capable(CAP_SYS_ADMIN)) {
Alan Cox44b7d1b2008-07-16 21:57:18 +01001991 if ((tmp.close_delay != port->port.close_delay) ||
1992 (tmp.closing_wait != port->port.closing_wait) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001993 ((tmp.flags & ~ASYNC_USR_MASK) !=
Alan Cox44b7d1b2008-07-16 21:57:18 +01001994 (port->port.flags & ~ASYNC_USR_MASK))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001995 func_exit();
Alan Coxb190e172008-04-30 00:53:22 -07001996 unlock_kernel();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001997 return -EPERM;
1998 }
Alan Cox44b7d1b2008-07-16 21:57:18 +01001999 port->port.flags = ((port->port.flags & ~ASYNC_USR_MASK) |
Alan Coxa72492b2008-07-22 11:17:05 +01002000 (tmp.flags & ASYNC_USR_MASK));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002001 port->custom_divisor = tmp.custom_divisor;
2002 } else {
Alan Cox44b7d1b2008-07-16 21:57:18 +01002003 port->port.flags = ((port->port.flags & ~ASYNC_FLAGS) |
Alan Coxa72492b2008-07-22 11:17:05 +01002004 (tmp.flags & ASYNC_FLAGS));
Alan Cox44b7d1b2008-07-16 21:57:18 +01002005 port->port.close_delay = tmp.close_delay;
2006 port->port.closing_wait = tmp.closing_wait;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002007 port->custom_divisor = tmp.custom_divisor;
2008 }
Alan Coxa72492b2008-07-22 11:17:05 +01002009 if (change_speed)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002010 sx_change_speed(bp, port);
Alan Coxa72492b2008-07-22 11:17:05 +01002011
Linus Torvalds1da177e2005-04-16 15:20:36 -07002012 func_exit();
Alan Coxb190e172008-04-30 00:53:22 -07002013 unlock_kernel();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002014 return 0;
2015}
2016
2017
Alan Coxa72492b2008-07-22 11:17:05 +01002018static inline int sx_get_serial_info(struct specialix_port *port,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002019 struct serial_struct __user *retinfo)
2020{
2021 struct serial_struct tmp;
2022 struct specialix_board *bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002023
Linus Torvalds1da177e2005-04-16 15:20:36 -07002024 func_enter();
2025
Linus Torvalds1da177e2005-04-16 15:20:36 -07002026 memset(&tmp, 0, sizeof(tmp));
Alan Coxb190e172008-04-30 00:53:22 -07002027 lock_kernel();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002028 tmp.type = PORT_CIRRUS;
2029 tmp.line = port - sx_port;
2030 tmp.port = bp->base;
2031 tmp.irq = bp->irq;
Alan Cox44b7d1b2008-07-16 21:57:18 +01002032 tmp.flags = port->port.flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002033 tmp.baud_base = (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC;
Alan Cox44b7d1b2008-07-16 21:57:18 +01002034 tmp.close_delay = port->port.close_delay * HZ/100;
2035 tmp.closing_wait = port->port.closing_wait * HZ/100;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002036 tmp.custom_divisor = port->custom_divisor;
2037 tmp.xmit_fifo_size = CD186x_NFIFO;
Alan Coxb190e172008-04-30 00:53:22 -07002038 unlock_kernel();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002039 if (copy_to_user(retinfo, &tmp, sizeof(tmp))) {
2040 func_exit();
2041 return -EFAULT;
2042 }
2043
2044 func_exit();
2045 return 0;
2046}
2047
2048
Alan Coxa72492b2008-07-22 11:17:05 +01002049static int sx_ioctl(struct tty_struct *tty, struct file *filp,
2050 unsigned int cmd, unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002051{
2052 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2053 int retval;
2054 void __user *argp = (void __user *)arg;
2055
2056 func_enter();
2057
2058 if (sx_paranoia_check(port, tty->name, "sx_ioctl")) {
2059 func_exit();
2060 return -ENODEV;
2061 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002062
Linus Torvalds1da177e2005-04-16 15:20:36 -07002063 switch (cmd) {
Alan Coxa72492b2008-07-22 11:17:05 +01002064 case TCSBRK: /* SVID version: non-zero arg --> no break */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002065 retval = tty_check_change(tty);
2066 if (retval) {
2067 func_exit();
2068 return retval;
2069 }
2070 tty_wait_until_sent(tty, 0);
2071 if (!arg)
2072 sx_send_break(port, HZ/4); /* 1/4 second */
2073 return 0;
Alan Coxa72492b2008-07-22 11:17:05 +01002074 case TCSBRKP: /* support for POSIX tcsendbreak() */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002075 retval = tty_check_change(tty);
2076 if (retval) {
2077 func_exit();
2078 return retval;
2079 }
2080 tty_wait_until_sent(tty, 0);
2081 sx_send_break(port, arg ? arg*(HZ/10) : HZ/4);
2082 func_exit();
2083 return 0;
Alan Coxa72492b2008-07-22 11:17:05 +01002084 case TIOCGSERIAL:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002085 func_exit();
2086 return sx_get_serial_info(port, argp);
Alan Coxa72492b2008-07-22 11:17:05 +01002087 case TIOCSSERIAL:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002088 func_exit();
2089 return sx_set_serial_info(port, argp);
Alan Coxa72492b2008-07-22 11:17:05 +01002090 default:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002091 func_exit();
2092 return -ENOIOCTLCMD;
2093 }
2094 func_exit();
2095 return 0;
2096}
2097
2098
Alan Coxa72492b2008-07-22 11:17:05 +01002099static void sx_throttle(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002100{
2101 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2102 struct specialix_board *bp;
2103 unsigned long flags;
2104
2105 func_enter();
2106
2107 if (sx_paranoia_check(port, tty->name, "sx_throttle")) {
2108 func_exit();
2109 return;
2110 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002111
Linus Torvalds1da177e2005-04-16 15:20:36 -07002112 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002113
Linus Torvalds1da177e2005-04-16 15:20:36 -07002114 /* Use DTR instead of RTS ! */
Alan Coxa72492b2008-07-22 11:17:05 +01002115 if (SX_CRTSCTS(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002116 port->MSVR &= ~MSVR_DTR;
2117 else {
2118 /* Auch!!! I think the system shouldn't call this then. */
2119 /* Or maybe we're supposed (allowed?) to do our side of hw
Jeff Garzikd61780c2005-10-30 15:01:51 -08002120 handshake anyway, even when hardware handshake is off.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002121 When you see this in your logs, please report.... */
Alan Coxa72492b2008-07-22 11:17:05 +01002122 printk(KERN_ERR
2123 "sx%d: Need to throttle, but can't (hardware hs is off)\n",
2124 port_No(port));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002125 }
2126 spin_lock_irqsave(&bp->lock, flags);
2127 sx_out(bp, CD186x_CAR, port_No(port));
2128 spin_unlock_irqrestore(&bp->lock, flags);
2129 if (I_IXOFF(tty)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002130 sx_wait_CCR(bp);
2131 spin_lock_irqsave(&bp->lock, flags);
2132 sx_out(bp, CD186x_CCR, CCR_SSCH2);
2133 spin_unlock_irqrestore(&bp->lock, flags);
2134 sx_wait_CCR(bp);
2135 }
2136 spin_lock_irqsave(&bp->lock, flags);
2137 sx_out(bp, CD186x_MSVR, port->MSVR);
2138 spin_unlock_irqrestore(&bp->lock, flags);
2139
2140 func_exit();
2141}
2142
2143
Alan Coxa72492b2008-07-22 11:17:05 +01002144static void sx_unthrottle(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002145{
2146 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2147 struct specialix_board *bp;
2148 unsigned long flags;
2149
2150 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002151
Linus Torvalds1da177e2005-04-16 15:20:36 -07002152 if (sx_paranoia_check(port, tty->name, "sx_unthrottle")) {
2153 func_exit();
2154 return;
2155 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002156
Linus Torvalds1da177e2005-04-16 15:20:36 -07002157 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002158
Linus Torvalds1da177e2005-04-16 15:20:36 -07002159 spin_lock_irqsave(&port->lock, flags);
2160 /* XXXX Use DTR INSTEAD???? */
Alan Coxa72492b2008-07-22 11:17:05 +01002161 if (SX_CRTSCTS(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002162 port->MSVR |= MSVR_DTR;
Alan Coxa72492b2008-07-22 11:17:05 +01002163 /* Else clause: see remark in "sx_throttle"... */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002164 spin_lock_irqsave(&bp->lock, flags);
2165 sx_out(bp, CD186x_CAR, port_No(port));
2166 spin_unlock_irqrestore(&bp->lock, flags);
2167 if (I_IXOFF(tty)) {
2168 spin_unlock_irqrestore(&port->lock, flags);
2169 sx_wait_CCR(bp);
2170 spin_lock_irqsave(&bp->lock, flags);
2171 sx_out(bp, CD186x_CCR, CCR_SSCH1);
2172 spin_unlock_irqrestore(&bp->lock, flags);
2173 sx_wait_CCR(bp);
2174 spin_lock_irqsave(&port->lock, flags);
2175 }
2176 spin_lock_irqsave(&bp->lock, flags);
2177 sx_out(bp, CD186x_MSVR, port->MSVR);
2178 spin_unlock_irqrestore(&bp->lock, flags);
2179 spin_unlock_irqrestore(&port->lock, flags);
2180
2181 func_exit();
2182}
2183
2184
Alan Coxa72492b2008-07-22 11:17:05 +01002185static void sx_stop(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002186{
2187 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2188 struct specialix_board *bp;
2189 unsigned long flags;
2190
2191 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002192
Linus Torvalds1da177e2005-04-16 15:20:36 -07002193 if (sx_paranoia_check(port, tty->name, "sx_stop")) {
2194 func_exit();
2195 return;
2196 }
2197
2198 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002199
Linus Torvalds1da177e2005-04-16 15:20:36 -07002200 spin_lock_irqsave(&port->lock, flags);
2201 port->IER &= ~IER_TXRDY;
2202 spin_lock_irqsave(&bp->lock, flags);
2203 sx_out(bp, CD186x_CAR, port_No(port));
2204 sx_out(bp, CD186x_IER, port->IER);
2205 spin_unlock_irqrestore(&bp->lock, flags);
2206 spin_unlock_irqrestore(&port->lock, flags);
2207
2208 func_exit();
2209}
2210
2211
Alan Coxa72492b2008-07-22 11:17:05 +01002212static void sx_start(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002213{
2214 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2215 struct specialix_board *bp;
2216 unsigned long flags;
2217
2218 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002219
Linus Torvalds1da177e2005-04-16 15:20:36 -07002220 if (sx_paranoia_check(port, tty->name, "sx_start")) {
2221 func_exit();
2222 return;
2223 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002224
Linus Torvalds1da177e2005-04-16 15:20:36 -07002225 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002226
Linus Torvalds1da177e2005-04-16 15:20:36 -07002227 spin_lock_irqsave(&port->lock, flags);
2228 if (port->xmit_cnt && port->xmit_buf && !(port->IER & IER_TXRDY)) {
2229 port->IER |= IER_TXRDY;
2230 spin_lock_irqsave(&bp->lock, flags);
2231 sx_out(bp, CD186x_CAR, port_No(port));
2232 sx_out(bp, CD186x_IER, port->IER);
2233 spin_unlock_irqrestore(&bp->lock, flags);
2234 }
2235 spin_unlock_irqrestore(&port->lock, flags);
2236
2237 func_exit();
2238}
2239
Alan Coxa72492b2008-07-22 11:17:05 +01002240static void sx_hangup(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002241{
2242 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2243 struct specialix_board *bp;
2244 unsigned long flags;
2245
2246 func_enter();
2247
2248 if (sx_paranoia_check(port, tty->name, "sx_hangup")) {
2249 func_exit();
2250 return;
2251 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002252
Linus Torvalds1da177e2005-04-16 15:20:36 -07002253 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002254
Linus Torvalds1da177e2005-04-16 15:20:36 -07002255 sx_shutdown_port(bp, port);
2256 spin_lock_irqsave(&port->lock, flags);
Alan Cox44b7d1b2008-07-16 21:57:18 +01002257 bp->count -= port->port.count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002258 if (bp->count < 0) {
Alan Coxa72492b2008-07-22 11:17:05 +01002259 printk(KERN_ERR
2260 "sx%d: sx_hangup: bad board count: %d port: %d\n",
2261 board_No(bp), bp->count, tty->index);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002262 bp->count = 0;
2263 }
Alan Cox44b7d1b2008-07-16 21:57:18 +01002264 port->port.count = 0;
2265 port->port.flags &= ~ASYNC_NORMAL_ACTIVE;
2266 port->port.tty = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002267 spin_unlock_irqrestore(&port->lock, flags);
Alan Cox44b7d1b2008-07-16 21:57:18 +01002268 wake_up_interruptible(&port->port.open_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002269
2270 func_exit();
2271}
2272
2273
Alan Coxa72492b2008-07-22 11:17:05 +01002274static void sx_set_termios(struct tty_struct *tty,
2275 struct ktermios *old_termios)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002276{
2277 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2278 unsigned long flags;
Alan Coxa72492b2008-07-22 11:17:05 +01002279 struct specialix_board *bp;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002280
Linus Torvalds1da177e2005-04-16 15:20:36 -07002281 if (sx_paranoia_check(port, tty->name, "sx_set_termios"))
2282 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002283
Linus Torvalds1da177e2005-04-16 15:20:36 -07002284 bp = port_Board(port);
2285 spin_lock_irqsave(&port->lock, flags);
2286 sx_change_speed(port_Board(port), port);
2287 spin_unlock_irqrestore(&port->lock, flags);
2288
2289 if ((old_termios->c_cflag & CRTSCTS) &&
2290 !(tty->termios->c_cflag & CRTSCTS)) {
2291 tty->hw_stopped = 0;
2292 sx_start(tty);
2293 }
2294}
2295
Jeff Dikeb68e31d2006-10-02 02:17:18 -07002296static const struct tty_operations sx_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002297 .open = sx_open,
2298 .close = sx_close,
2299 .write = sx_write,
2300 .put_char = sx_put_char,
2301 .flush_chars = sx_flush_chars,
2302 .write_room = sx_write_room,
2303 .chars_in_buffer = sx_chars_in_buffer,
2304 .flush_buffer = sx_flush_buffer,
2305 .ioctl = sx_ioctl,
2306 .throttle = sx_throttle,
2307 .unthrottle = sx_unthrottle,
2308 .set_termios = sx_set_termios,
2309 .stop = sx_stop,
2310 .start = sx_start,
2311 .hangup = sx_hangup,
2312 .tiocmget = sx_tiocmget,
2313 .tiocmset = sx_tiocmset,
2314};
2315
2316static int sx_init_drivers(void)
2317{
2318 int error;
2319 int i;
2320
2321 func_enter();
2322
2323 specialix_driver = alloc_tty_driver(SX_NBOARD * SX_NPORT);
2324 if (!specialix_driver) {
2325 printk(KERN_ERR "sx: Couldn't allocate tty_driver.\n");
2326 func_exit();
2327 return 1;
2328 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002329
Linus Torvalds1da177e2005-04-16 15:20:36 -07002330 specialix_driver->owner = THIS_MODULE;
2331 specialix_driver->name = "ttyW";
2332 specialix_driver->major = SPECIALIX_NORMAL_MAJOR;
2333 specialix_driver->type = TTY_DRIVER_TYPE_SERIAL;
2334 specialix_driver->subtype = SERIAL_TYPE_NORMAL;
2335 specialix_driver->init_termios = tty_std_termios;
2336 specialix_driver->init_termios.c_cflag =
2337 B9600 | CS8 | CREAD | HUPCL | CLOCAL;
Alan Cox606d0992006-12-08 02:38:45 -08002338 specialix_driver->init_termios.c_ispeed = 9600;
2339 specialix_driver->init_termios.c_ospeed = 9600;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002340 specialix_driver->flags = TTY_DRIVER_REAL_RAW;
2341 tty_set_operations(specialix_driver, &sx_ops);
2342
Alan Coxa72492b2008-07-22 11:17:05 +01002343 error = tty_register_driver(specialix_driver);
2344 if (error) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002345 put_tty_driver(specialix_driver);
Alan Coxa72492b2008-07-22 11:17:05 +01002346 printk(KERN_ERR
2347 "sx: Couldn't register specialix IO8+ driver, error = %d\n",
2348 error);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002349 func_exit();
2350 return 1;
2351 }
2352 memset(sx_port, 0, sizeof(sx_port));
2353 for (i = 0; i < SX_NPORT * SX_NBOARD; i++) {
2354 sx_port[i].magic = SPECIALIX_MAGIC;
Alan Cox44b7d1b2008-07-16 21:57:18 +01002355 tty_port_init(&sx_port[i].port);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002356 spin_lock_init(&sx_port[i].lock);
2357 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002358
Linus Torvalds1da177e2005-04-16 15:20:36 -07002359 func_exit();
2360 return 0;
2361}
2362
2363static void sx_release_drivers(void)
2364{
2365 func_enter();
2366
Linus Torvalds1da177e2005-04-16 15:20:36 -07002367 tty_unregister_driver(specialix_driver);
2368 put_tty_driver(specialix_driver);
2369 func_exit();
2370}
2371
Jeff Garzikd61780c2005-10-30 15:01:51 -08002372/*
2373 * This routine must be called by kernel at boot time
Linus Torvalds1da177e2005-04-16 15:20:36 -07002374 */
2375static int __init specialix_init(void)
2376{
2377 int i;
2378 int found = 0;
2379
2380 func_enter();
2381
2382 printk(KERN_INFO "sx: Specialix IO8+ driver v" VERSION ", (c) R.E.Wolff 1997/1998.\n");
2383 printk(KERN_INFO "sx: derived from work (c) D.Gorodchanin 1994-1996.\n");
2384#ifdef CONFIG_SPECIALIX_RTSCTS
Alan Coxa72492b2008-07-22 11:17:05 +01002385 printk(KERN_INFO "sx: DTR/RTS pin is always RTS.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002386#else
Alan Coxa72492b2008-07-22 11:17:05 +01002387 printk(KERN_INFO "sx: DTR/RTS pin is RTS when CRTSCTS is on.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002388#endif
Jeff Garzikd61780c2005-10-30 15:01:51 -08002389
Linus Torvalds1da177e2005-04-16 15:20:36 -07002390 for (i = 0; i < SX_NBOARD; i++)
Ingo Molnar34af9462006-06-27 02:53:55 -07002391 spin_lock_init(&sx_board[i].lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002392
2393 if (sx_init_drivers()) {
2394 func_exit();
2395 return -EIO;
2396 }
2397
Jeff Garzikd61780c2005-10-30 15:01:51 -08002398 for (i = 0; i < SX_NBOARD; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002399 if (sx_board[i].base && !sx_probe(&sx_board[i]))
2400 found++;
2401
2402#ifdef CONFIG_PCI
2403 {
2404 struct pci_dev *pdev = NULL;
2405
Alan Coxa72492b2008-07-22 11:17:05 +01002406 i = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002407 while (i < SX_NBOARD) {
2408 if (sx_board[i].flags & SX_BOARD_PRESENT) {
2409 i++;
2410 continue;
2411 }
Alan Coxa72492b2008-07-22 11:17:05 +01002412 pdev = pci_get_device(PCI_VENDOR_ID_SPECIALIX,
2413 PCI_DEVICE_ID_SPECIALIX_IO8, pdev);
2414 if (!pdev)
2415 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002416
2417 if (pci_enable_device(pdev))
2418 continue;
2419
2420 sx_board[i].irq = pdev->irq;
2421
Alan Coxa72492b2008-07-22 11:17:05 +01002422 sx_board[i].base = pci_resource_start(pdev, 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002423
2424 sx_board[i].flags |= SX_BOARD_IS_PCI;
2425 if (!sx_probe(&sx_board[i]))
Alan Coxa72492b2008-07-22 11:17:05 +01002426 found++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002427 }
Alan Cox606d0992006-12-08 02:38:45 -08002428 /* May exit pci_get sequence early with lots of boards */
2429 if (pdev != NULL)
2430 pci_dev_put(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002431 }
2432#endif
2433
2434 if (!found) {
2435 sx_release_drivers();
2436 printk(KERN_INFO "sx: No specialix IO8+ boards detected.\n");
2437 func_exit();
2438 return -EIO;
2439 }
2440
2441 func_exit();
2442 return 0;
2443}
2444
2445static int iobase[SX_NBOARD] = {0,};
2446
2447static int irq [SX_NBOARD] = {0,};
2448
2449module_param_array(iobase, int, NULL, 0);
2450module_param_array(irq, int, NULL, 0);
2451module_param(sx_debug, int, 0);
2452module_param(sx_rxfifo, int, 0);
2453#ifdef SPECIALIX_TIMER
2454module_param(sx_poll, int, 0);
2455#endif
2456
2457/*
2458 * You can setup up to 4 boards.
2459 * by specifying "iobase=0xXXX,0xXXX ..." as insmod parameter.
Jeff Garzikd61780c2005-10-30 15:01:51 -08002460 * You should specify the IRQs too in that case "irq=....,...".
2461 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07002462 * More than 4 boards in one computer is not possible, as the card can
Jeff Garzikd61780c2005-10-30 15:01:51 -08002463 * only use 4 different interrupts.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002464 *
2465 */
2466static int __init specialix_init_module(void)
2467{
2468 int i;
2469
2470 func_enter();
2471
Linus Torvalds1da177e2005-04-16 15:20:36 -07002472 if (iobase[0] || iobase[1] || iobase[2] || iobase[3]) {
Alan Coxa72492b2008-07-22 11:17:05 +01002473 for (i = 0; i < SX_NBOARD; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002474 sx_board[i].base = iobase[i];
2475 sx_board[i].irq = irq[i];
Alan Coxa72492b2008-07-22 11:17:05 +01002476 sx_board[i].count = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002477 }
2478 }
2479
2480 func_exit();
2481
2482 return specialix_init();
2483}
Jeff Garzikd61780c2005-10-30 15:01:51 -08002484
Linus Torvalds1da177e2005-04-16 15:20:36 -07002485static void __exit specialix_exit_module(void)
2486{
2487 int i;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002488
Linus Torvalds1da177e2005-04-16 15:20:36 -07002489 func_enter();
2490
2491 sx_release_drivers();
2492 for (i = 0; i < SX_NBOARD; i++)
Jeff Garzikd61780c2005-10-30 15:01:51 -08002493 if (sx_board[i].flags & SX_BOARD_PRESENT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002494 sx_release_io_range(&sx_board[i]);
2495#ifdef SPECIALIX_TIMER
Jiri Slaby40565f12007-02-12 00:52:31 -08002496 del_timer_sync(&missed_irq_timer);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002497#endif
2498
2499 func_exit();
2500}
2501
Chuck Short76910302006-07-10 04:43:59 -07002502static struct pci_device_id specialx_pci_tbl[] __devinitdata = {
2503 { PCI_DEVICE(PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_SPECIALIX_IO8) },
2504 { }
2505};
2506MODULE_DEVICE_TABLE(pci, specialx_pci_tbl);
2507
Linus Torvalds1da177e2005-04-16 15:20:36 -07002508module_init(specialix_init_module);
2509module_exit(specialix_exit_module);
2510
2511MODULE_LICENSE("GPL");