blob: 07ac14d949ce2f12dc94ab6e3b3c0a7ae3a32931 [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 Garzikd61780c02005-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 Garzikd61780c02005-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 Garzikd61780c02005-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 Garzikd61780c02005-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 Garzikd61780c02005-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 Garzikd61780c02005-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
Randy Dunlap31c00fc2008-11-13 21:33:24 +000075 * Documentation/serial/specialix.txt
Linus Torvalds1da177e2005-04-16 15:20:36 -070076 */
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>
Alexey Dobriyan405f5572009-07-11 22:08:37 +040090#include <linux/smp_lock.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070091#include <linux/fcntl.h>
92#include <linux/major.h>
93#include <linux/delay.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070094#include <linux/pci.h>
95#include <linux/init.h>
Alan Coxa72492b2008-07-22 11:17:05 +010096#include <linux/uaccess.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070097
98#include "specialix_io8.h"
99#include "cd1865.h"
100
101
102/*
103 This driver can spew a whole lot of debugging output at you. If you
104 need maximum performance, you should disable the DEBUG define. To
105 aid in debugging in the field, I'm leaving the compile-time debug
106 features enabled, and disable them "runtime". That allows me to
107 instruct people with problems to enable debugging without requiring
108 them to recompile...
109*/
110#define DEBUG
111
112static int sx_debug;
113static int sx_rxfifo = SPECIALIX_RXFIFO;
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100114static int sx_rtscts;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115
116#ifdef DEBUG
Alan Coxa72492b2008-07-22 11:17:05 +0100117#define dprintk(f, str...) if (sx_debug & f) printk(str)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118#else
119#define dprintk(f, str...) /* nothing */
120#endif
121
122#define SX_DEBUG_FLOW 0x0001
123#define SX_DEBUG_DATA 0x0002
124#define SX_DEBUG_PROBE 0x0004
125#define SX_DEBUG_CHAN 0x0008
126#define SX_DEBUG_INIT 0x0010
127#define SX_DEBUG_RX 0x0020
128#define SX_DEBUG_TX 0x0040
129#define SX_DEBUG_IRQ 0x0080
130#define SX_DEBUG_OPEN 0x0100
131#define SX_DEBUG_TERMIOS 0x0200
132#define SX_DEBUG_SIGNALS 0x0400
133#define SX_DEBUG_FIFO 0x0800
134
135
Alan Coxa72492b2008-07-22 11:17:05 +0100136#define func_enter() dprintk(SX_DEBUG_FLOW, "io8: enter %s\n", __func__)
137#define func_exit() dprintk(SX_DEBUG_FLOW, "io8: exit %s\n", __func__)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139
140/* Configurable options: */
141
142/* Am I paranoid or not ? ;-) */
143#define SPECIALIX_PARANOIA_CHECK
144
Jeff Garzikd61780c02005-10-30 15:01:51 -0800145/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 * The following defines are mostly for testing purposes. But if you need
147 * some nice reporting in your syslog, you can define them also.
148 */
149#undef SX_REPORT_FIFO
150#undef SX_REPORT_OVERRUN
151
152
153
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154
155#define SPECIALIX_LEGAL_FLAGS \
156 (ASYNC_HUP_NOTIFY | ASYNC_SAK | ASYNC_SPLIT_TERMIOS | \
157 ASYNC_SPD_HI | ASYNC_SPEED_VHI | ASYNC_SESSION_LOCKOUT | \
158 ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP)
159
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160static struct tty_driver *specialix_driver;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162static struct specialix_board sx_board[SX_NBOARD] = {
163 { 0, SX_IOBASE1, 9, },
164 { 0, SX_IOBASE2, 11, },
165 { 0, SX_IOBASE3, 12, },
166 { 0, SX_IOBASE4, 15, },
167};
168
169static struct specialix_port sx_port[SX_NBOARD * SX_NPORT];
170
171
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100172static int sx_paranoia_check(struct specialix_port const *port,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 char *name, const char *routine)
174{
175#ifdef SPECIALIX_PARANOIA_CHECK
Alan Coxa72492b2008-07-22 11:17:05 +0100176 static const char *badmagic = KERN_ERR
177 "sx: Warning: bad specialix port magic number for device %s in %s\n";
178 static const char *badinfo = KERN_ERR
179 "sx: Warning: null specialix port for device %s in %s\n";
Jeff Garzikd61780c02005-10-30 15:01:51 -0800180
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181 if (!port) {
182 printk(badinfo, name, routine);
183 return 1;
184 }
185 if (port->magic != SPECIALIX_MAGIC) {
186 printk(badmagic, name, routine);
187 return 1;
188 }
189#endif
190 return 0;
191}
192
193
194/*
Jeff Garzikd61780c02005-10-30 15:01:51 -0800195 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196 * Service functions for specialix IO8+ driver.
Jeff Garzikd61780c02005-10-30 15:01:51 -0800197 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198 */
199
200/* Get board number from pointer */
Alan Coxa72492b2008-07-22 11:17:05 +0100201static inline int board_No(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202{
203 return bp - sx_board;
204}
205
206
207/* Get port number from pointer */
Alan Coxa72492b2008-07-22 11:17:05 +0100208static inline int port_No(struct specialix_port const *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209{
Jeff Garzikd61780c02005-10-30 15:01:51 -0800210 return SX_PORT(port - sx_port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211}
212
213
214/* Get pointer to board from pointer to port */
Alan Coxa72492b2008-07-22 11:17:05 +0100215static inline struct specialix_board *port_Board(
216 struct specialix_port const *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217{
218 return &sx_board[SX_BOARD(port - sx_port)];
219}
220
221
222/* Input Byte from CL CD186x register */
Alan Coxa72492b2008-07-22 11:17:05 +0100223static inline unsigned char sx_in(struct specialix_board *bp,
224 unsigned short reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225{
226 bp->reg = reg | 0x80;
Alan Coxa72492b2008-07-22 11:17:05 +0100227 outb(reg | 0x80, bp->base + SX_ADDR_REG);
228 return inb(bp->base + SX_DATA_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229}
230
231
232/* Output Byte to CL CD186x register */
Alan Coxa72492b2008-07-22 11:17:05 +0100233static inline void sx_out(struct specialix_board *bp, unsigned short reg,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234 unsigned char val)
235{
236 bp->reg = reg | 0x80;
Alan Coxa72492b2008-07-22 11:17:05 +0100237 outb(reg | 0x80, bp->base + SX_ADDR_REG);
238 outb(val, bp->base + SX_DATA_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239}
240
241
242/* Input Byte from CL CD186x register */
Alan Coxa72492b2008-07-22 11:17:05 +0100243static inline unsigned char sx_in_off(struct specialix_board *bp,
244 unsigned short reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245{
246 bp->reg = reg;
Alan Coxa72492b2008-07-22 11:17:05 +0100247 outb(reg, bp->base + SX_ADDR_REG);
248 return inb(bp->base + SX_DATA_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249}
250
251
252/* Output Byte to CL CD186x register */
Alan Coxa72492b2008-07-22 11:17:05 +0100253static inline void sx_out_off(struct specialix_board *bp,
254 unsigned short reg, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255{
256 bp->reg = reg;
Alan Coxa72492b2008-07-22 11:17:05 +0100257 outb(reg, bp->base + SX_ADDR_REG);
258 outb(val, bp->base + SX_DATA_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259}
260
261
262/* Wait for Channel Command Register ready */
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100263static void sx_wait_CCR(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264{
265 unsigned long delay, flags;
266 unsigned char ccr;
267
268 for (delay = SX_CCR_TIMEOUT; delay; delay--) {
269 spin_lock_irqsave(&bp->lock, flags);
270 ccr = sx_in(bp, CD186x_CCR);
271 spin_unlock_irqrestore(&bp->lock, flags);
272 if (!ccr)
273 return;
Alan Coxa72492b2008-07-22 11:17:05 +0100274 udelay(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 }
Jeff Garzikd61780c02005-10-30 15:01:51 -0800276
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277 printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp));
278}
279
280
281/* Wait for Channel Command Register ready */
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100282static void sx_wait_CCR_off(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283{
284 unsigned long delay;
285 unsigned char crr;
286 unsigned long flags;
287
288 for (delay = SX_CCR_TIMEOUT; delay; delay--) {
289 spin_lock_irqsave(&bp->lock, flags);
290 crr = sx_in_off(bp, CD186x_CCR);
291 spin_unlock_irqrestore(&bp->lock, flags);
292 if (!crr)
293 return;
Alan Coxa72492b2008-07-22 11:17:05 +0100294 udelay(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 }
Jeff Garzikd61780c02005-10-30 15:01:51 -0800296
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297 printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp));
298}
299
300
301/*
302 * specialix IO8+ IO range functions.
303 */
304
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100305static int sx_request_io_range(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306{
Jeff Garzikd61780c02005-10-30 15:01:51 -0800307 return request_region(bp->base,
308 bp->flags & SX_BOARD_IS_PCI ? SX_PCI_IO_SPACE : SX_IO_SPACE,
309 "specialix IO8+") == NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310}
311
312
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100313static void sx_release_io_range(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314{
Alan Coxa72492b2008-07-22 11:17:05 +0100315 release_region(bp->base, bp->flags & SX_BOARD_IS_PCI ?
316 SX_PCI_IO_SPACE : SX_IO_SPACE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317}
318
Jeff Garzikd61780c02005-10-30 15:01:51 -0800319
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320/* Set the IRQ using the RTS lines that run to the PAL on the board.... */
Alan Coxa72492b2008-07-22 11:17:05 +0100321static int sx_set_irq(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322{
323 int virq;
324 int i;
325 unsigned long flags;
326
Jeff Garzikd61780c02005-10-30 15:01:51 -0800327 if (bp->flags & SX_BOARD_IS_PCI)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328 return 1;
329 switch (bp->irq) {
330 /* In the same order as in the docs... */
Alan Coxa72492b2008-07-22 11:17:05 +0100331 case 15:
332 virq = 0;
333 break;
334 case 12:
335 virq = 1;
336 break;
337 case 11:
338 virq = 2;
339 break;
340 case 9:
341 virq = 3;
342 break;
343 default:printk(KERN_ERR
344 "Speclialix: cannot set irq to %d.\n", bp->irq);
345 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346 }
347 spin_lock_irqsave(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +0100348 for (i = 0; i < 2; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349 sx_out(bp, CD186x_CAR, i);
350 sx_out(bp, CD186x_MSVRTS, ((virq >> i) & 0x1)? MSVR_RTS:0);
351 }
352 spin_unlock_irqrestore(&bp->lock, flags);
353 return 1;
354}
355
356
357/* Reset and setup CD186x chip */
Alan Coxa72492b2008-07-22 11:17:05 +0100358static int sx_init_CD186x(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359{
360 unsigned long flags;
361 int scaler;
362 int rv = 1;
363
364 func_enter();
365 sx_wait_CCR_off(bp); /* Wait for CCR ready */
366 spin_lock_irqsave(&bp->lock, flags);
367 sx_out_off(bp, CD186x_CCR, CCR_HARDRESET); /* Reset CD186x chip */
368 spin_unlock_irqrestore(&bp->lock, flags);
Jiri Slaby3e98cee2007-07-17 04:05:19 -0700369 msleep(50); /* Delay 0.05 sec */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 spin_lock_irqsave(&bp->lock, flags);
371 sx_out_off(bp, CD186x_GIVR, SX_ID); /* Set ID for this chip */
372 sx_out_off(bp, CD186x_GICR, 0); /* Clear all bits */
373 sx_out_off(bp, CD186x_PILR1, SX_ACK_MINT); /* Prio for modem intr */
374 sx_out_off(bp, CD186x_PILR2, SX_ACK_TINT); /* Prio for transmitter intr */
375 sx_out_off(bp, CD186x_PILR3, SX_ACK_RINT); /* Prio for receiver intr */
376 /* Set RegAckEn */
Alan Coxa72492b2008-07-22 11:17:05 +0100377 sx_out_off(bp, CD186x_SRCR, sx_in(bp, CD186x_SRCR) | SRCR_REGACKEN);
Jeff Garzikd61780c02005-10-30 15:01:51 -0800378
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379 /* Setting up prescaler. We need 4 ticks per 1 ms */
380 scaler = SX_OSCFREQ/SPECIALIX_TPS;
381
382 sx_out_off(bp, CD186x_PPRH, scaler >> 8);
383 sx_out_off(bp, CD186x_PPRL, scaler & 0xff);
384 spin_unlock_irqrestore(&bp->lock, flags);
385
Alan Coxa72492b2008-07-22 11:17:05 +0100386 if (!sx_set_irq(bp)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387 /* Figure out how to pass this along... */
Alan Coxa72492b2008-07-22 11:17:05 +0100388 printk(KERN_ERR "Cannot set irq to %d.\n", bp->irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389 rv = 0;
390 }
391
392 func_exit();
393 return rv;
394}
395
396
Alan Coxa72492b2008-07-22 11:17:05 +0100397static int read_cross_byte(struct specialix_board *bp, int reg, int bit)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398{
399 int i;
400 int t;
401 unsigned long flags;
402
403 spin_lock_irqsave(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +0100404 for (i = 0, t = 0; i < 8; i++) {
405 sx_out_off(bp, CD186x_CAR, i);
406 if (sx_in_off(bp, reg) & bit)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407 t |= 1 << i;
408 }
409 spin_unlock_irqrestore(&bp->lock, flags);
410
411 return t;
412}
413
414
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415/* Main probing routine, also sets irq. */
416static int sx_probe(struct specialix_board *bp)
417{
418 unsigned char val1, val2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419 int rev;
420 int chip;
421
422 func_enter();
423
Jeff Garzikd61780c02005-10-30 15:01:51 -0800424 if (sx_request_io_range(bp)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425 func_exit();
426 return 1;
427 }
428
429 /* Are the I/O ports here ? */
430 sx_out_off(bp, CD186x_PPRL, 0x5a);
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100431 udelay(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 val1 = sx_in_off(bp, CD186x_PPRL);
433
434 sx_out_off(bp, CD186x_PPRL, 0xa5);
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100435 udelay(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436 val2 = sx_in_off(bp, CD186x_PPRL);
437
Jeff Garzikd61780c02005-10-30 15:01:51 -0800438
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100439 if (val1 != 0x5a || val2 != 0xa5) {
Alan Coxa72492b2008-07-22 11:17:05 +0100440 printk(KERN_INFO
441 "sx%d: specialix IO8+ Board at 0x%03x not found.\n",
442 board_No(bp), bp->base);
Jeff Garzikd61780c02005-10-30 15:01:51 -0800443 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444 func_exit();
445 return 1;
446 }
447
Jeff Garzikd61780c02005-10-30 15:01:51 -0800448 /* Check the DSR lines that Specialix uses as board
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449 identification */
Alan Coxa72492b2008-07-22 11:17:05 +0100450 val1 = read_cross_byte(bp, CD186x_MSVR, MSVR_DSR);
451 val2 = read_cross_byte(bp, CD186x_MSVR, MSVR_RTS);
452 dprintk(SX_DEBUG_INIT,
453 "sx%d: DSR lines are: %02x, rts lines are: %02x\n",
454 board_No(bp), val1, val2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455
456 /* They managed to switch the bit order between the docs and
457 the IO8+ card. The new PCI card now conforms to old docs.
458 They changed the PCI docs to reflect the situation on the
459 old card. */
460 val2 = (bp->flags & SX_BOARD_IS_PCI)?0x4d : 0xb2;
461 if (val1 != val2) {
Alan Coxa72492b2008-07-22 11:17:05 +0100462 printk(KERN_INFO
463 "sx%d: specialix IO8+ ID %02x at 0x%03x not found (%02x).\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464 board_No(bp), val2, bp->base, val1);
Jeff Garzikd61780c02005-10-30 15:01:51 -0800465 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466 func_exit();
467 return 1;
468 }
469
470
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 /* Reset CD186x again */
472 if (!sx_init_CD186x(bp)) {
Jeff Garzikd61780c02005-10-30 15:01:51 -0800473 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 func_exit();
Jeff Garzikd61780c02005-10-30 15:01:51 -0800475 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476 }
477
478 sx_request_io_range(bp);
479 bp->flags |= SX_BOARD_PRESENT;
Jeff Garzikd61780c02005-10-30 15:01:51 -0800480
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481 /* Chip revcode pkgtype
Alan Coxa72492b2008-07-22 11:17:05 +0100482 GFRCR SRCR bit 7
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483 CD180 rev B 0x81 0
484 CD180 rev C 0x82 0
485 CD1864 rev A 0x82 1
Jeff Garzikd61780c02005-10-30 15:01:51 -0800486 CD1865 rev A 0x83 1 -- Do not use!!! Does not work.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487 CD1865 rev B 0x84 1
488 -- Thanks to Gwen Wang, Cirrus Logic.
489 */
490
491 switch (sx_in_off(bp, CD186x_GFRCR)) {
Alan Coxa72492b2008-07-22 11:17:05 +0100492 case 0x82:
493 chip = 1864;
494 rev = 'A';
495 break;
496 case 0x83:
497 chip = 1865;
498 rev = 'A';
499 break;
500 case 0x84:
501 chip = 1865;
502 rev = 'B';
503 break;
504 case 0x85:
505 chip = 1865;
506 rev = 'C';
507 break; /* Does not exist at this time */
508 default:
509 chip = -1;
510 rev = 'x';
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 }
512
Alan Coxa72492b2008-07-22 11:17:05 +0100513 dprintk(SX_DEBUG_INIT, " GFCR = 0x%02x\n", sx_in_off(bp, CD186x_GFRCR));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514
Alan Coxa72492b2008-07-22 11:17:05 +0100515 printk(KERN_INFO
516 "sx%d: specialix IO8+ board detected at 0x%03x, IRQ %d, CD%d Rev. %c.\n",
517 board_No(bp), bp->base, bp->irq, chip, rev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518
519 func_exit();
520 return 0;
521}
522
Jeff Garzikd61780c02005-10-30 15:01:51 -0800523/*
524 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525 * Interrupt processing routines.
526 * */
527
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100528static struct specialix_port *sx_get_port(struct specialix_board *bp,
Alan Coxa72492b2008-07-22 11:17:05 +0100529 unsigned char const *what)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530{
531 unsigned char channel;
Alan Coxa72492b2008-07-22 11:17:05 +0100532 struct specialix_port *port = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533
534 channel = sx_in(bp, CD186x_GICR) >> GICR_CHAN_OFF;
Alan Coxa72492b2008-07-22 11:17:05 +0100535 dprintk(SX_DEBUG_CHAN, "channel: %d\n", channel);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 if (channel < CD186x_NCH) {
537 port = &sx_port[board_No(bp) * SX_NPORT + channel];
Alan Coxa72492b2008-07-22 11:17:05 +0100538 dprintk(SX_DEBUG_CHAN, "port: %d %p flags: 0x%lx\n",
539 board_No(bp) * SX_NPORT + channel, port,
540 port->port.flags & ASYNC_INITIALIZED);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541
Alan Cox44b7d1b2008-07-16 21:57:18 +0100542 if (port->port.flags & ASYNC_INITIALIZED) {
Alan Coxa72492b2008-07-22 11:17:05 +0100543 dprintk(SX_DEBUG_CHAN, "port: %d %p\n", channel, port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 func_exit();
545 return port;
546 }
547 }
Jeff Garzikd61780c02005-10-30 15:01:51 -0800548 printk(KERN_INFO "sx%d: %s interrupt from invalid port %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 board_No(bp), what, channel);
550 return NULL;
551}
552
553
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100554static void sx_receive_exc(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555{
556 struct specialix_port *port;
557 struct tty_struct *tty;
558 unsigned char status;
Alan Cox33f0f882006-01-09 20:54:13 -0800559 unsigned char ch, flag;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560
561 func_enter();
562
563 port = sx_get_port(bp, "Receive");
564 if (!port) {
Alan Coxa72492b2008-07-22 11:17:05 +0100565 dprintk(SX_DEBUG_RX, "Hmm, couldn't find port.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 func_exit();
567 return;
568 }
Alan Cox44b7d1b2008-07-16 21:57:18 +0100569 tty = port->port.tty;
Jeff Garzikd61780c02005-10-30 15:01:51 -0800570
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571 status = sx_in(bp, CD186x_RCSR);
572
Alan Coxa72492b2008-07-22 11:17:05 +0100573 dprintk(SX_DEBUG_RX, "status: 0x%x\n", status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574 if (status & RCSR_OE) {
575 port->overrun++;
Alan Coxa72492b2008-07-22 11:17:05 +0100576 dprintk(SX_DEBUG_FIFO,
577 "sx%d: port %d: Overrun. Total %ld overruns.\n",
578 board_No(bp), port_No(port), port->overrun);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 }
580 status &= port->mark_mask;
581
582 /* This flip buffer check needs to be below the reading of the
583 status register to reset the chip's IRQ.... */
Alan Cox33f0f882006-01-09 20:54:13 -0800584 if (tty_buffer_request_room(tty, 1) == 0) {
Alan Coxa72492b2008-07-22 11:17:05 +0100585 dprintk(SX_DEBUG_FIFO,
586 "sx%d: port %d: Working around flip buffer overflow.\n",
587 board_No(bp), port_No(port));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588 func_exit();
589 return;
590 }
591
592 ch = sx_in(bp, CD186x_RDR);
593 if (!status) {
594 func_exit();
595 return;
596 }
597 if (status & RCSR_TOUT) {
Alan Coxa72492b2008-07-22 11:17:05 +0100598 printk(KERN_INFO
599 "sx%d: port %d: Receiver timeout. Hardware problems ?\n",
600 board_No(bp), port_No(port));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601 func_exit();
602 return;
Jeff Garzikd61780c02005-10-30 15:01:51 -0800603
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604 } else if (status & RCSR_BREAK) {
605 dprintk(SX_DEBUG_RX, "sx%d: port %d: Handling break...\n",
606 board_No(bp), port_No(port));
Alan Cox33f0f882006-01-09 20:54:13 -0800607 flag = TTY_BREAK;
Alan Cox44b7d1b2008-07-16 21:57:18 +0100608 if (port->port.flags & ASYNC_SAK)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609 do_SAK(tty);
Jeff Garzikd61780c02005-10-30 15:01:51 -0800610
611 } else if (status & RCSR_PE)
Alan Cox33f0f882006-01-09 20:54:13 -0800612 flag = TTY_PARITY;
Jeff Garzikd61780c02005-10-30 15:01:51 -0800613
614 else if (status & RCSR_FE)
Alan Cox33f0f882006-01-09 20:54:13 -0800615 flag = TTY_FRAME;
Jeff Garzikd61780c02005-10-30 15:01:51 -0800616
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617 else if (status & RCSR_OE)
Alan Cox33f0f882006-01-09 20:54:13 -0800618 flag = TTY_OVERRUN;
Jeff Garzikd61780c02005-10-30 15:01:51 -0800619
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 else
Alan Cox33f0f882006-01-09 20:54:13 -0800621 flag = TTY_NORMAL;
Jeff Garzikd61780c02005-10-30 15:01:51 -0800622
Alan Coxa72492b2008-07-22 11:17:05 +0100623 if (tty_insert_flip_char(tty, ch, flag))
Alan Cox33f0f882006-01-09 20:54:13 -0800624 tty_flip_buffer_push(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625 func_exit();
626}
627
628
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100629static void sx_receive(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630{
631 struct specialix_port *port;
632 struct tty_struct *tty;
633 unsigned char count;
634
635 func_enter();
Jeff Garzikd61780c02005-10-30 15:01:51 -0800636
Alan Coxa72492b2008-07-22 11:17:05 +0100637 port = sx_get_port(bp, "Receive");
638 if (port == NULL) {
639 dprintk(SX_DEBUG_RX, "Hmm, couldn't find port.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640 func_exit();
641 return;
642 }
Alan Cox44b7d1b2008-07-16 21:57:18 +0100643 tty = port->port.tty;
Jeff Garzikd61780c02005-10-30 15:01:51 -0800644
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645 count = sx_in(bp, CD186x_RDCR);
Alan Coxa72492b2008-07-22 11:17:05 +0100646 dprintk(SX_DEBUG_RX, "port: %p: count: %d\n", port, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647 port->hits[count > 8 ? 9 : count]++;
Jeff Garzikd61780c02005-10-30 15:01:51 -0800648
Alan Cox33f0f882006-01-09 20:54:13 -0800649 while (count--)
650 tty_insert_flip_char(tty, sx_in(bp, CD186x_RDR), TTY_NORMAL);
651 tty_flip_buffer_push(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652 func_exit();
653}
654
655
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100656static void sx_transmit(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657{
658 struct specialix_port *port;
659 struct tty_struct *tty;
660 unsigned char count;
661
662 func_enter();
Alan Coxa72492b2008-07-22 11:17:05 +0100663 port = sx_get_port(bp, "Transmit");
664 if (port == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 func_exit();
666 return;
667 }
Alan Coxa72492b2008-07-22 11:17:05 +0100668 dprintk(SX_DEBUG_TX, "port: %p\n", port);
Alan Cox44b7d1b2008-07-16 21:57:18 +0100669 tty = port->port.tty;
Jeff Garzikd61780c02005-10-30 15:01:51 -0800670
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671 if (port->IER & IER_TXEMPTY) {
672 /* FIFO drained */
673 sx_out(bp, CD186x_CAR, port_No(port));
674 port->IER &= ~IER_TXEMPTY;
675 sx_out(bp, CD186x_IER, port->IER);
676 func_exit();
677 return;
678 }
Jeff Garzikd61780c02005-10-30 15:01:51 -0800679
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 if ((port->xmit_cnt <= 0 && !port->break_length)
681 || tty->stopped || tty->hw_stopped) {
682 sx_out(bp, CD186x_CAR, port_No(port));
683 port->IER &= ~IER_TXRDY;
684 sx_out(bp, CD186x_IER, port->IER);
685 func_exit();
686 return;
687 }
Jeff Garzikd61780c02005-10-30 15:01:51 -0800688
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689 if (port->break_length) {
690 if (port->break_length > 0) {
691 if (port->COR2 & COR2_ETC) {
692 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
693 sx_out(bp, CD186x_TDR, CD186x_C_SBRK);
694 port->COR2 &= ~COR2_ETC;
695 }
696 count = min_t(int, port->break_length, 0xff);
697 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
698 sx_out(bp, CD186x_TDR, CD186x_C_DELAY);
699 sx_out(bp, CD186x_TDR, count);
Alan Coxa72492b2008-07-22 11:17:05 +0100700 port->break_length -= count;
701 if (port->break_length == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702 port->break_length--;
703 } else {
704 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
705 sx_out(bp, CD186x_TDR, CD186x_C_EBRK);
706 sx_out(bp, CD186x_COR2, port->COR2);
707 sx_wait_CCR(bp);
708 sx_out(bp, CD186x_CCR, CCR_CORCHG2);
709 port->break_length = 0;
710 }
711
712 func_exit();
713 return;
714 }
Jeff Garzikd61780c02005-10-30 15:01:51 -0800715
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716 count = CD186x_NFIFO;
717 do {
718 sx_out(bp, CD186x_TDR, port->xmit_buf[port->xmit_tail++]);
719 port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1);
720 if (--port->xmit_cnt <= 0)
721 break;
722 } while (--count > 0);
Jeff Garzikd61780c02005-10-30 15:01:51 -0800723
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 if (port->xmit_cnt <= 0) {
725 sx_out(bp, CD186x_CAR, port_No(port));
726 port->IER &= ~IER_TXRDY;
727 sx_out(bp, CD186x_IER, port->IER);
728 }
729 if (port->xmit_cnt <= port->wakeup_chars)
Alan Coxa72492b2008-07-22 11:17:05 +0100730 tty_wakeup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731
732 func_exit();
733}
734
735
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100736static void sx_check_modem(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737{
738 struct specialix_port *port;
739 struct tty_struct *tty;
740 unsigned char mcr;
741 int msvr_cd;
742
Alan Coxa72492b2008-07-22 11:17:05 +0100743 dprintk(SX_DEBUG_SIGNALS, "Modem intr. ");
744 port = sx_get_port(bp, "Modem");
745 if (port == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746 return;
Jeff Garzikd61780c02005-10-30 15:01:51 -0800747
Alan Cox44b7d1b2008-07-16 21:57:18 +0100748 tty = port->port.tty;
Jeff Garzikd61780c02005-10-30 15:01:51 -0800749
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750 mcr = sx_in(bp, CD186x_MCR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751
752 if ((mcr & MCR_CDCHG)) {
Alan Coxa72492b2008-07-22 11:17:05 +0100753 dprintk(SX_DEBUG_SIGNALS, "CD just changed... ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754 msvr_cd = sx_in(bp, CD186x_MSVR) & MSVR_CD;
755 if (msvr_cd) {
Alan Coxa72492b2008-07-22 11:17:05 +0100756 dprintk(SX_DEBUG_SIGNALS, "Waking up guys in open.\n");
Alan Cox44b7d1b2008-07-16 21:57:18 +0100757 wake_up_interruptible(&port->port.open_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758 } else {
Alan Coxa72492b2008-07-22 11:17:05 +0100759 dprintk(SX_DEBUG_SIGNALS, "Sending HUP.\n");
Jiri Slabyd0d4e1c2008-02-07 00:16:39 -0800760 tty_hangup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761 }
762 }
Jeff Garzikd61780c02005-10-30 15:01:51 -0800763
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
765 if (mcr & MCR_CTSCHG) {
766 if (sx_in(bp, CD186x_MSVR) & MSVR_CTS) {
767 tty->hw_stopped = 0;
768 port->IER |= IER_TXRDY;
769 if (port->xmit_cnt <= port->wakeup_chars)
Jiri Slabyd0d4e1c2008-02-07 00:16:39 -0800770 tty_wakeup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771 } else {
772 tty->hw_stopped = 1;
773 port->IER &= ~IER_TXRDY;
774 }
775 sx_out(bp, CD186x_IER, port->IER);
776 }
777 if (mcr & MCR_DSSXHG) {
778 if (sx_in(bp, CD186x_MSVR) & MSVR_DSR) {
779 tty->hw_stopped = 0;
780 port->IER |= IER_TXRDY;
781 if (port->xmit_cnt <= port->wakeup_chars)
Jiri Slabyd0d4e1c2008-02-07 00:16:39 -0800782 tty_wakeup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 } else {
784 tty->hw_stopped = 1;
785 port->IER &= ~IER_TXRDY;
786 }
787 sx_out(bp, CD186x_IER, port->IER);
788 }
789#endif /* SPECIALIX_BRAIN_DAMAGED_CTS */
Jeff Garzikd61780c02005-10-30 15:01:51 -0800790
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 /* Clear change bits */
792 sx_out(bp, CD186x_MCR, 0);
793}
794
795
796/* The main interrupt processing routine */
Jeff Garzika6f97b22007-10-31 05:20:49 -0400797static irqreturn_t sx_interrupt(int dummy, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798{
799 unsigned char status;
800 unsigned char ack;
Jeff Garzika6f97b22007-10-31 05:20:49 -0400801 struct specialix_board *bp = dev_id;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802 unsigned long loop = 0;
803 int saved_reg;
804 unsigned long flags;
805
806 func_enter();
807
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808 spin_lock_irqsave(&bp->lock, flags);
809
Alan Coxa72492b2008-07-22 11:17:05 +0100810 dprintk(SX_DEBUG_FLOW, "enter %s port %d room: %ld\n", __func__,
811 port_No(sx_get_port(bp, "INT")),
812 SERIAL_XMIT_SIZE - sx_get_port(bp, "ITN")->xmit_cnt - 1);
Jeff Garzikc7bec5a2006-10-06 15:00:58 -0400813 if (!(bp->flags & SX_BOARD_ACTIVE)) {
Alan Coxa72492b2008-07-22 11:17:05 +0100814 dprintk(SX_DEBUG_IRQ, "sx: False interrupt. irq %d.\n",
815 bp->irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 spin_unlock_irqrestore(&bp->lock, flags);
817 func_exit();
818 return IRQ_NONE;
819 }
820
821 saved_reg = bp->reg;
822
Alan Coxa72492b2008-07-22 11:17:05 +0100823 while (++loop < 16) {
824 status = sx_in(bp, CD186x_SRSR) &
825 (SRSR_RREQint | SRSR_TREQint | SRSR_MREQint);
826 if (status == 0)
827 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 if (status & SRSR_RREQint) {
829 ack = sx_in(bp, CD186x_RRAR);
830
831 if (ack == (SX_ID | GIVR_IT_RCV))
832 sx_receive(bp);
833 else if (ack == (SX_ID | GIVR_IT_REXC))
834 sx_receive_exc(bp);
835 else
Alan Coxa72492b2008-07-22 11:17:05 +0100836 printk(KERN_ERR
837 "sx%d: status: 0x%x Bad receive ack 0x%02x.\n",
838 board_No(bp), status, ack);
Jeff Garzikd61780c02005-10-30 15:01:51 -0800839
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840 } else if (status & SRSR_TREQint) {
841 ack = sx_in(bp, CD186x_TRAR);
842
843 if (ack == (SX_ID | GIVR_IT_TX))
844 sx_transmit(bp);
845 else
846 printk(KERN_ERR "sx%d: status: 0x%x Bad transmit ack 0x%02x. port: %d\n",
Alan Coxa72492b2008-07-22 11:17:05 +0100847 board_No(bp), status, ack,
848 port_No(sx_get_port(bp, "Int")));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849 } else if (status & SRSR_MREQint) {
850 ack = sx_in(bp, CD186x_MRAR);
851
Jeff Garzikd61780c02005-10-30 15:01:51 -0800852 if (ack == (SX_ID | GIVR_IT_MODEM))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853 sx_check_modem(bp);
854 else
Alan Coxa72492b2008-07-22 11:17:05 +0100855 printk(KERN_ERR
856 "sx%d: status: 0x%x Bad modem ack 0x%02x.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857 board_No(bp), status, ack);
Jeff Garzikd61780c02005-10-30 15:01:51 -0800858
859 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860
861 sx_out(bp, CD186x_EOIR, 0); /* Mark end of interrupt */
862 }
863 bp->reg = saved_reg;
Alan Coxa72492b2008-07-22 11:17:05 +0100864 outb(bp->reg, bp->base + SX_ADDR_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 spin_unlock_irqrestore(&bp->lock, flags);
866 func_exit();
867 return IRQ_HANDLED;
868}
869
870
871/*
872 * Routines for open & close processing.
873 */
874
Alan Coxa72492b2008-07-22 11:17:05 +0100875static void turn_ints_off(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876{
877 unsigned long flags;
878
879 func_enter();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880 spin_lock_irqsave(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +0100881 (void) sx_in_off(bp, 0); /* Turn off interrupts. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700882 spin_unlock_irqrestore(&bp->lock, flags);
883
884 func_exit();
885}
886
Alan Coxa72492b2008-07-22 11:17:05 +0100887static void turn_ints_on(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888{
889 unsigned long flags;
890
891 func_enter();
892
Linus Torvalds1da177e2005-04-16 15:20:36 -0700893 spin_lock_irqsave(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +0100894 (void) sx_in(bp, 0); /* Turn ON interrupts. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895 spin_unlock_irqrestore(&bp->lock, flags);
896
897 func_exit();
898}
899
900
901/* Called with disabled interrupts */
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100902static int sx_setup_board(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903{
904 int error;
905
Jeff Garzikd61780c02005-10-30 15:01:51 -0800906 if (bp->flags & SX_BOARD_ACTIVE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907 return 0;
908
909 if (bp->flags & SX_BOARD_IS_PCI)
Alan Coxa72492b2008-07-22 11:17:05 +0100910 error = request_irq(bp->irq, sx_interrupt,
911 IRQF_DISABLED | IRQF_SHARED, "specialix IO8+", bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912 else
Alan Coxa72492b2008-07-22 11:17:05 +0100913 error = request_irq(bp->irq, sx_interrupt,
914 IRQF_DISABLED, "specialix IO8+", bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915
Jeff Garzikd61780c02005-10-30 15:01:51 -0800916 if (error)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917 return error;
918
Alan Coxa72492b2008-07-22 11:17:05 +0100919 turn_ints_on(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920 bp->flags |= SX_BOARD_ACTIVE;
921
922 return 0;
923}
924
925
926/* Called with disabled interrupts */
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100927static void sx_shutdown_board(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700928{
929 func_enter();
930
931 if (!(bp->flags & SX_BOARD_ACTIVE)) {
932 func_exit();
933 return;
934 }
935
936 bp->flags &= ~SX_BOARD_ACTIVE;
Jeff Garzikd61780c02005-10-30 15:01:51 -0800937
Alan Coxa72492b2008-07-22 11:17:05 +0100938 dprintk(SX_DEBUG_IRQ, "Freeing IRQ%d for board %d.\n",
939 bp->irq, board_No(bp));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940 free_irq(bp->irq, bp);
Alan Coxa72492b2008-07-22 11:17:05 +0100941 turn_ints_off(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942 func_exit();
943}
944
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100945static unsigned int sx_crtscts(struct tty_struct *tty)
946{
947 if (sx_rtscts)
948 return C_CRTSCTS(tty);
949 return 1;
950}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951
952/*
Jeff Garzikd61780c02005-10-30 15:01:51 -0800953 * Setting up port characteristics.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954 * Must be called with disabled interrupts
955 */
Alan Coxa72492b2008-07-22 11:17:05 +0100956static void sx_change_speed(struct specialix_board *bp,
957 struct specialix_port *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958{
959 struct tty_struct *tty;
960 unsigned long baud;
961 long tmp;
962 unsigned char cor1 = 0, cor3 = 0;
963 unsigned char mcor1 = 0, mcor2 = 0;
964 static unsigned long again;
965 unsigned long flags;
966
967 func_enter();
968
Alan Coxa72492b2008-07-22 11:17:05 +0100969 tty = port->port.tty;
970 if (!tty || !tty->termios) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971 func_exit();
972 return;
973 }
974
975 port->IER = 0;
976 port->COR2 = 0;
977 /* Select port on the board */
978 spin_lock_irqsave(&bp->lock, flags);
979 sx_out(bp, CD186x_CAR, port_No(port));
980
981 /* The Specialix board doens't implement the RTS lines.
982 They are used to set the IRQ level. Don't touch them. */
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100983 if (sx_crtscts(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984 port->MSVR = MSVR_DTR | (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
985 else
986 port->MSVR = (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
987 spin_unlock_irqrestore(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +0100988 dprintk(SX_DEBUG_TERMIOS, "sx: got MSVR=%02x.\n", port->MSVR);
Alan Cox67cc0162006-09-29 02:01:39 -0700989 baud = tty_get_baud_rate(tty);
Jeff Garzikd61780c02005-10-30 15:01:51 -0800990
Alan Cox67cc0162006-09-29 02:01:39 -0700991 if (baud == 38400) {
Alan Cox44b7d1b2008-07-16 21:57:18 +0100992 if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
Adrian Bunka4bb2cf2006-10-17 00:10:00 -0700993 baud = 57600;
Alan Cox44b7d1b2008-07-16 21:57:18 +0100994 if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
Adrian Bunka4bb2cf2006-10-17 00:10:00 -0700995 baud = 115200;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700996 }
Jeff Garzikd61780c02005-10-30 15:01:51 -0800997
Alan Cox67cc0162006-09-29 02:01:39 -0700998 if (!baud) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999 /* Drop DTR & exit */
Alan Coxa72492b2008-07-22 11:17:05 +01001000 dprintk(SX_DEBUG_TERMIOS, "Dropping DTR... Hmm....\n");
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001001 if (!sx_crtscts(tty)) {
Alan Coxa72492b2008-07-22 11:17:05 +01001002 port->MSVR &= ~MSVR_DTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003 spin_lock_irqsave(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001004 sx_out(bp, CD186x_MSVR, port->MSVR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005 spin_unlock_irqrestore(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001006 } else
1007 dprintk(SX_DEBUG_TERMIOS, "Can't drop DTR: no DTR.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008 return;
1009 } else {
1010 /* Set DTR on */
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001011 if (!sx_crtscts(tty))
Alan Coxa72492b2008-07-22 11:17:05 +01001012 port->MSVR |= MSVR_DTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013 }
Jeff Garzikd61780c02005-10-30 15:01:51 -08001014
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015 /*
Jeff Garzikd61780c02005-10-30 15:01:51 -08001016 * Now we must calculate some speed depended things
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017 */
1018
1019 /* Set baud rate for port */
1020 tmp = port->custom_divisor ;
Alan Coxa72492b2008-07-22 11:17:05 +01001021 if (tmp)
1022 printk(KERN_INFO
1023 "sx%d: Using custom baud rate divisor %ld. \n"
1024 "This is an untested option, please be careful.\n",
1025 port_No(port), tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001026 else
Alan Coxa72492b2008-07-22 11:17:05 +01001027 tmp = (((SX_OSCFREQ + baud/2) / baud + CD186x_TPC/2) /
1028 CD186x_TPC);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029
Alan Coxa72492b2008-07-22 11:17:05 +01001030 if (tmp < 0x10 && time_before(again, jiffies)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031 again = jiffies + HZ * 60;
1032 /* Page 48 of version 2.0 of the CL-CD1865 databook */
1033 if (tmp >= 12) {
Alan Coxa72492b2008-07-22 11:17:05 +01001034 printk(KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
1035 "Performance degradation is possible.\n"
1036 "Read specialix.txt for more info.\n",
1037 port_No(port), tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038 } else {
Alan Coxa72492b2008-07-22 11:17:05 +01001039 printk(KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
1040 "Warning: overstressing Cirrus chip. This might not work.\n"
1041 "Read specialix.txt for more info.\n", port_No(port), tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042 }
1043 }
1044 spin_lock_irqsave(&bp->lock, flags);
Jeff Garzikd61780c02005-10-30 15:01:51 -08001045 sx_out(bp, CD186x_RBPRH, (tmp >> 8) & 0xff);
1046 sx_out(bp, CD186x_TBPRH, (tmp >> 8) & 0xff);
1047 sx_out(bp, CD186x_RBPRL, tmp & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048 sx_out(bp, CD186x_TBPRL, tmp & 0xff);
1049 spin_unlock_irqrestore(&bp->lock, flags);
Adrian Bunka4bb2cf2006-10-17 00:10:00 -07001050 if (port->custom_divisor)
Alan Coxa72492b2008-07-22 11:17:05 +01001051 baud = (SX_OSCFREQ + port->custom_divisor/2) /
1052 port->custom_divisor;
Adrian Bunka4bb2cf2006-10-17 00:10:00 -07001053 baud = (baud + 5) / 10; /* Estimated CPS */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001054
1055 /* Two timer ticks seems enough to wakeup something like SLIP driver */
Jeff Garzikd61780c02005-10-30 15:01:51 -08001056 tmp = ((baud + HZ/2) / HZ) * 2 - CD186x_NFIFO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057 port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ?
1058 SERIAL_XMIT_SIZE - 1 : tmp);
Jeff Garzikd61780c02005-10-30 15:01:51 -08001059
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060 /* Receiver timeout will be transmission time for 1.5 chars */
1061 tmp = (SPECIALIX_TPS + SPECIALIX_TPS/2 + baud/2) / baud;
1062 tmp = (tmp > 0xff) ? 0xff : tmp;
1063 spin_lock_irqsave(&bp->lock, flags);
1064 sx_out(bp, CD186x_RTPR, tmp);
1065 spin_unlock_irqrestore(&bp->lock, flags);
1066 switch (C_CSIZE(tty)) {
Alan Coxa72492b2008-07-22 11:17:05 +01001067 case CS5:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068 cor1 |= COR1_5BITS;
1069 break;
Alan Coxa72492b2008-07-22 11:17:05 +01001070 case CS6:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071 cor1 |= COR1_6BITS;
1072 break;
Alan Coxa72492b2008-07-22 11:17:05 +01001073 case CS7:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074 cor1 |= COR1_7BITS;
1075 break;
Alan Coxa72492b2008-07-22 11:17:05 +01001076 case CS8:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077 cor1 |= COR1_8BITS;
1078 break;
1079 }
Jeff Garzikd61780c02005-10-30 15:01:51 -08001080
1081 if (C_CSTOPB(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 cor1 |= COR1_2SB;
Jeff Garzikd61780c02005-10-30 15:01:51 -08001083
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084 cor1 |= COR1_IGNORE;
1085 if (C_PARENB(tty)) {
1086 cor1 |= COR1_NORMPAR;
Jeff Garzikd61780c02005-10-30 15:01:51 -08001087 if (C_PARODD(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088 cor1 |= COR1_ODDP;
Jeff Garzikd61780c02005-10-30 15:01:51 -08001089 if (I_INPCK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090 cor1 &= ~COR1_IGNORE;
1091 }
1092 /* Set marking of some errors */
1093 port->mark_mask = RCSR_OE | RCSR_TOUT;
Jeff Garzikd61780c02005-10-30 15:01:51 -08001094 if (I_INPCK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095 port->mark_mask |= RCSR_FE | RCSR_PE;
Jeff Garzikd61780c02005-10-30 15:01:51 -08001096 if (I_BRKINT(tty) || I_PARMRK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001097 port->mark_mask |= RCSR_BREAK;
Jeff Garzikd61780c02005-10-30 15:01:51 -08001098 if (I_IGNPAR(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001099 port->mark_mask &= ~(RCSR_FE | RCSR_PE);
1100 if (I_IGNBRK(tty)) {
1101 port->mark_mask &= ~RCSR_BREAK;
Jeff Garzikd61780c02005-10-30 15:01:51 -08001102 if (I_IGNPAR(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001103 /* Real raw mode. Ignore all */
1104 port->mark_mask &= ~RCSR_OE;
1105 }
1106 /* Enable Hardware Flow Control */
1107 if (C_CRTSCTS(tty)) {
1108#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
1109 port->IER |= IER_DSR | IER_CTS;
1110 mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD;
1111 mcor2 |= MCOR2_DSROD | MCOR2_CTSOD;
1112 spin_lock_irqsave(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001113 tty->hw_stopped = !(sx_in(bp, CD186x_MSVR) &
1114 (MSVR_CTS|MSVR_DSR));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001115 spin_unlock_irqrestore(&bp->lock, flags);
1116#else
Jeff Garzikd61780c02005-10-30 15:01:51 -08001117 port->COR2 |= COR2_CTSAE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118#endif
1119 }
1120 /* Enable Software Flow Control. FIXME: I'm not sure about this */
1121 /* Some people reported that it works, but I still doubt it */
1122 if (I_IXON(tty)) {
1123 port->COR2 |= COR2_TXIBE;
1124 cor3 |= (COR3_FCT | COR3_SCDE);
1125 if (I_IXANY(tty))
1126 port->COR2 |= COR2_IXM;
1127 spin_lock_irqsave(&bp->lock, flags);
1128 sx_out(bp, CD186x_SCHR1, START_CHAR(tty));
1129 sx_out(bp, CD186x_SCHR2, STOP_CHAR(tty));
1130 sx_out(bp, CD186x_SCHR3, START_CHAR(tty));
1131 sx_out(bp, CD186x_SCHR4, STOP_CHAR(tty));
1132 spin_unlock_irqrestore(&bp->lock, flags);
1133 }
1134 if (!C_CLOCAL(tty)) {
1135 /* Enable CD check */
1136 port->IER |= IER_CD;
1137 mcor1 |= MCOR1_CDZD;
1138 mcor2 |= MCOR2_CDOD;
1139 }
Jeff Garzikd61780c02005-10-30 15:01:51 -08001140
1141 if (C_CREAD(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142 /* Enable receiver */
1143 port->IER |= IER_RXD;
Jeff Garzikd61780c02005-10-30 15:01:51 -08001144
Linus Torvalds1da177e2005-04-16 15:20:36 -07001145 /* Set input FIFO size (1-8 bytes) */
1146 cor3 |= sx_rxfifo;
1147 /* Setting up CD186x channel registers */
1148 spin_lock_irqsave(&bp->lock, flags);
1149 sx_out(bp, CD186x_COR1, cor1);
1150 sx_out(bp, CD186x_COR2, port->COR2);
1151 sx_out(bp, CD186x_COR3, cor3);
1152 spin_unlock_irqrestore(&bp->lock, flags);
1153 /* Make CD186x know about registers change */
1154 sx_wait_CCR(bp);
1155 spin_lock_irqsave(&bp->lock, flags);
1156 sx_out(bp, CD186x_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3);
1157 /* Setting up modem option registers */
Alan Coxa72492b2008-07-22 11:17:05 +01001158 dprintk(SX_DEBUG_TERMIOS, "Mcor1 = %02x, mcor2 = %02x.\n",
1159 mcor1, mcor2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 sx_out(bp, CD186x_MCOR1, mcor1);
1161 sx_out(bp, CD186x_MCOR2, mcor2);
1162 spin_unlock_irqrestore(&bp->lock, flags);
1163 /* Enable CD186x transmitter & receiver */
1164 sx_wait_CCR(bp);
1165 spin_lock_irqsave(&bp->lock, flags);
1166 sx_out(bp, CD186x_CCR, CCR_TXEN | CCR_RXEN);
1167 /* Enable interrupts */
1168 sx_out(bp, CD186x_IER, port->IER);
1169 /* And finally set the modem lines... */
1170 sx_out(bp, CD186x_MSVR, port->MSVR);
1171 spin_unlock_irqrestore(&bp->lock, flags);
1172
1173 func_exit();
1174}
1175
1176
1177/* Must be called with interrupts enabled */
Alan Coxa72492b2008-07-22 11:17:05 +01001178static int sx_setup_port(struct specialix_board *bp,
1179 struct specialix_port *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001180{
1181 unsigned long flags;
1182
1183 func_enter();
1184
Alan Cox44b7d1b2008-07-16 21:57:18 +01001185 if (port->port.flags & ASYNC_INITIALIZED) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001186 func_exit();
1187 return 0;
1188 }
Jeff Garzikd61780c02005-10-30 15:01:51 -08001189
Linus Torvalds1da177e2005-04-16 15:20:36 -07001190 if (!port->xmit_buf) {
1191 /* We may sleep in get_zeroed_page() */
1192 unsigned long tmp;
Jeff Garzikd61780c02005-10-30 15:01:51 -08001193
Alan Coxa72492b2008-07-22 11:17:05 +01001194 tmp = get_zeroed_page(GFP_KERNEL);
1195 if (tmp == 0L) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196 func_exit();
1197 return -ENOMEM;
1198 }
1199
1200 if (port->xmit_buf) {
1201 free_page(tmp);
1202 func_exit();
1203 return -ERESTARTSYS;
1204 }
1205 port->xmit_buf = (unsigned char *) tmp;
1206 }
Jeff Garzikd61780c02005-10-30 15:01:51 -08001207
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208 spin_lock_irqsave(&port->lock, flags);
1209
Alan Cox44b7d1b2008-07-16 21:57:18 +01001210 if (port->port.tty)
1211 clear_bit(TTY_IO_ERROR, &port->port.tty->flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212
1213 port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
1214 sx_change_speed(bp, port);
Alan Cox44b7d1b2008-07-16 21:57:18 +01001215 port->port.flags |= ASYNC_INITIALIZED;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001216
1217 spin_unlock_irqrestore(&port->lock, flags);
1218
Jeff Garzikd61780c02005-10-30 15:01:51 -08001219
Linus Torvalds1da177e2005-04-16 15:20:36 -07001220 func_exit();
1221 return 0;
1222}
1223
1224
1225/* Must be called with interrupts disabled */
Alan Coxa72492b2008-07-22 11:17:05 +01001226static void sx_shutdown_port(struct specialix_board *bp,
1227 struct specialix_port *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001228{
1229 struct tty_struct *tty;
1230 int i;
1231 unsigned long flags;
Jeff Garzikd61780c02005-10-30 15:01:51 -08001232
Linus Torvalds1da177e2005-04-16 15:20:36 -07001233 func_enter();
1234
Alan Cox44b7d1b2008-07-16 21:57:18 +01001235 if (!(port->port.flags & ASYNC_INITIALIZED)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001236 func_exit();
1237 return;
1238 }
Jeff Garzikd61780c02005-10-30 15:01:51 -08001239
Linus Torvalds1da177e2005-04-16 15:20:36 -07001240 if (sx_debug & SX_DEBUG_FIFO) {
Alan Coxa72492b2008-07-22 11:17:05 +01001241 dprintk(SX_DEBUG_FIFO,
1242 "sx%d: port %d: %ld overruns, FIFO hits [ ",
1243 board_No(bp), port_No(port), port->overrun);
1244 for (i = 0; i < 10; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001245 dprintk(SX_DEBUG_FIFO, "%ld ", port->hits[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001246 dprintk(SX_DEBUG_FIFO, "].\n");
1247 }
1248
1249 if (port->xmit_buf) {
1250 free_page((unsigned long) port->xmit_buf);
1251 port->xmit_buf = NULL;
1252 }
1253
1254 /* Select port */
1255 spin_lock_irqsave(&bp->lock, flags);
1256 sx_out(bp, CD186x_CAR, port_No(port));
1257
Alan Coxa72492b2008-07-22 11:17:05 +01001258 tty = port->port.tty;
1259 if (tty == NULL || C_HUPCL(tty)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260 /* Drop DTR */
1261 sx_out(bp, CD186x_MSVDTR, 0);
1262 }
1263 spin_unlock_irqrestore(&bp->lock, flags);
1264 /* Reset port */
1265 sx_wait_CCR(bp);
1266 spin_lock_irqsave(&bp->lock, flags);
1267 sx_out(bp, CD186x_CCR, CCR_SOFTRESET);
1268 /* Disable all interrupts from this port */
1269 port->IER = 0;
1270 sx_out(bp, CD186x_IER, port->IER);
1271 spin_unlock_irqrestore(&bp->lock, flags);
1272 if (tty)
1273 set_bit(TTY_IO_ERROR, &tty->flags);
Alan Cox44b7d1b2008-07-16 21:57:18 +01001274 port->port.flags &= ~ASYNC_INITIALIZED;
Jeff Garzikd61780c02005-10-30 15:01:51 -08001275
1276 if (!bp->count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001277 sx_shutdown_board(bp);
1278 func_exit();
1279}
1280
Jeff Garzikd61780c02005-10-30 15:01:51 -08001281
Alan Coxa72492b2008-07-22 11:17:05 +01001282static int block_til_ready(struct tty_struct *tty, struct file *filp,
1283 struct specialix_port *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284{
1285 DECLARE_WAITQUEUE(wait, current);
1286 struct specialix_board *bp = port_Board(port);
1287 int retval;
1288 int do_clocal = 0;
1289 int CD;
1290 unsigned long flags;
1291
1292 func_enter();
1293
1294 /*
1295 * If the device is in the middle of being closed, then block
1296 * until it's done, and then try again.
1297 */
Alan Cox44b7d1b2008-07-16 21:57:18 +01001298 if (tty_hung_up_p(filp) || port->port.flags & ASYNC_CLOSING) {
1299 interruptible_sleep_on(&port->port.close_wait);
1300 if (port->port.flags & ASYNC_HUP_NOTIFY) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001301 func_exit();
1302 return -EAGAIN;
1303 } else {
1304 func_exit();
1305 return -ERESTARTSYS;
1306 }
1307 }
Jeff Garzikd61780c02005-10-30 15:01:51 -08001308
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309 /*
1310 * If non-blocking mode is set, or the port is not enabled,
1311 * then make the check up front and then exit.
1312 */
1313 if ((filp->f_flags & O_NONBLOCK) ||
1314 (tty->flags & (1 << TTY_IO_ERROR))) {
Alan Cox44b7d1b2008-07-16 21:57:18 +01001315 port->port.flags |= ASYNC_NORMAL_ACTIVE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316 func_exit();
1317 return 0;
1318 }
1319
1320 if (C_CLOCAL(tty))
1321 do_clocal = 1;
1322
1323 /*
1324 * Block waiting for the carrier detect and the line to become
1325 * free (i.e., not in use by the callout). While we are in
1326 * this loop, info->count is dropped by one, so that
1327 * rs_close() knows when to free things. We restore it upon
1328 * exit, either normal or abnormal.
1329 */
1330 retval = 0;
Alan Cox44b7d1b2008-07-16 21:57:18 +01001331 add_wait_queue(&port->port.open_wait, &wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332 spin_lock_irqsave(&port->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001333 if (!tty_hung_up_p(filp))
Alan Cox44b7d1b2008-07-16 21:57:18 +01001334 port->port.count--;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001335 spin_unlock_irqrestore(&port->lock, flags);
Alan Cox44b7d1b2008-07-16 21:57:18 +01001336 port->port.blocked_open++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337 while (1) {
1338 spin_lock_irqsave(&bp->lock, flags);
1339 sx_out(bp, CD186x_CAR, port_No(port));
1340 CD = sx_in(bp, CD186x_MSVR) & MSVR_CD;
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001341 if (sx_crtscts(tty)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001342 /* Activate RTS */
1343 port->MSVR |= MSVR_DTR; /* WTF? */
Alan Coxa72492b2008-07-22 11:17:05 +01001344 sx_out(bp, CD186x_MSVR, port->MSVR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001345 } else {
1346 /* Activate DTR */
1347 port->MSVR |= MSVR_DTR;
Alan Coxa72492b2008-07-22 11:17:05 +01001348 sx_out(bp, CD186x_MSVR, port->MSVR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349 }
1350 spin_unlock_irqrestore(&bp->lock, flags);
1351 set_current_state(TASK_INTERRUPTIBLE);
1352 if (tty_hung_up_p(filp) ||
Alan Cox44b7d1b2008-07-16 21:57:18 +01001353 !(port->port.flags & ASYNC_INITIALIZED)) {
1354 if (port->port.flags & ASYNC_HUP_NOTIFY)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355 retval = -EAGAIN;
1356 else
Jeff Garzikd61780c02005-10-30 15:01:51 -08001357 retval = -ERESTARTSYS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358 break;
1359 }
Alan Cox44b7d1b2008-07-16 21:57:18 +01001360 if (!(port->port.flags & ASYNC_CLOSING) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361 (do_clocal || CD))
1362 break;
1363 if (signal_pending(current)) {
1364 retval = -ERESTARTSYS;
1365 break;
1366 }
1367 schedule();
1368 }
1369
1370 set_current_state(TASK_RUNNING);
Alan Cox44b7d1b2008-07-16 21:57:18 +01001371 remove_wait_queue(&port->port.open_wait, &wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372 spin_lock_irqsave(&port->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001373 if (!tty_hung_up_p(filp))
Alan Cox44b7d1b2008-07-16 21:57:18 +01001374 port->port.count++;
Alan Cox44b7d1b2008-07-16 21:57:18 +01001375 port->port.blocked_open--;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376 spin_unlock_irqrestore(&port->lock, flags);
1377 if (retval) {
1378 func_exit();
1379 return retval;
1380 }
1381
Alan Cox44b7d1b2008-07-16 21:57:18 +01001382 port->port.flags |= ASYNC_NORMAL_ACTIVE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001383 func_exit();
1384 return 0;
Jeff Garzikd61780c02005-10-30 15:01:51 -08001385}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001386
1387
Alan Coxa72492b2008-07-22 11:17:05 +01001388static int sx_open(struct tty_struct *tty, struct file *filp)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389{
1390 int board;
1391 int error;
Alan Coxa72492b2008-07-22 11:17:05 +01001392 struct specialix_port *port;
1393 struct specialix_board *bp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001394 int i;
1395 unsigned long flags;
1396
1397 func_enter();
1398
1399 board = SX_BOARD(tty->index);
1400
1401 if (board >= SX_NBOARD || !(sx_board[board].flags & SX_BOARD_PRESENT)) {
1402 func_exit();
1403 return -ENODEV;
1404 }
Jeff Garzikd61780c02005-10-30 15:01:51 -08001405
Linus Torvalds1da177e2005-04-16 15:20:36 -07001406 bp = &sx_board[board];
1407 port = sx_port + board * SX_NPORT + SX_PORT(tty->index);
1408 port->overrun = 0;
1409 for (i = 0; i < 10; i++)
Alan Coxa72492b2008-07-22 11:17:05 +01001410 port->hits[i] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001411
Alan Coxa72492b2008-07-22 11:17:05 +01001412 dprintk(SX_DEBUG_OPEN,
1413 "Board = %d, bp = %p, port = %p, portno = %d.\n",
1414 board, bp, port, SX_PORT(tty->index));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001415
1416 if (sx_paranoia_check(port, tty->name, "sx_open")) {
1417 func_enter();
1418 return -ENODEV;
1419 }
1420
Alan Coxa72492b2008-07-22 11:17:05 +01001421 error = sx_setup_board(bp);
1422 if (error) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001423 func_exit();
1424 return error;
1425 }
1426
1427 spin_lock_irqsave(&bp->lock, flags);
Alan Cox44b7d1b2008-07-16 21:57:18 +01001428 port->port.count++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001429 bp->count++;
1430 tty->driver_data = port;
Alan Cox44b7d1b2008-07-16 21:57:18 +01001431 port->port.tty = tty;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001432 spin_unlock_irqrestore(&bp->lock, flags);
1433
Alan Coxa72492b2008-07-22 11:17:05 +01001434 error = sx_setup_port(bp, port);
1435 if (error) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001436 func_enter();
1437 return error;
1438 }
Jeff Garzikd61780c02005-10-30 15:01:51 -08001439
Alan Coxa72492b2008-07-22 11:17:05 +01001440 error = block_til_ready(tty, filp, port);
1441 if (error) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442 func_enter();
1443 return error;
1444 }
1445
1446 func_exit();
1447 return 0;
1448}
1449
Alan Cox978e5952008-04-30 00:53:59 -07001450static void sx_flush_buffer(struct tty_struct *tty)
1451{
Alan Coxc9f19e92009-01-02 13:47:26 +00001452 struct specialix_port *port = tty->driver_data;
Alan Cox978e5952008-04-30 00:53:59 -07001453 unsigned long flags;
Alan Coxa72492b2008-07-22 11:17:05 +01001454 struct specialix_board *bp;
Alan Cox978e5952008-04-30 00:53:59 -07001455
1456 func_enter();
1457
1458 if (sx_paranoia_check(port, tty->name, "sx_flush_buffer")) {
1459 func_exit();
1460 return;
1461 }
1462
1463 bp = port_Board(port);
1464 spin_lock_irqsave(&port->lock, flags);
1465 port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
1466 spin_unlock_irqrestore(&port->lock, flags);
1467 tty_wakeup(tty);
1468
1469 func_exit();
1470}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001471
Alan Coxa72492b2008-07-22 11:17:05 +01001472static void sx_close(struct tty_struct *tty, struct file *filp)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001473{
Alan Coxc9f19e92009-01-02 13:47:26 +00001474 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001475 struct specialix_board *bp;
1476 unsigned long flags;
1477 unsigned long timeout;
Jeff Garzikd61780c02005-10-30 15:01:51 -08001478
Linus Torvalds1da177e2005-04-16 15:20:36 -07001479 func_enter();
1480 if (!port || sx_paranoia_check(port, tty->name, "close")) {
1481 func_exit();
1482 return;
1483 }
1484 spin_lock_irqsave(&port->lock, flags);
1485
1486 if (tty_hung_up_p(filp)) {
1487 spin_unlock_irqrestore(&port->lock, flags);
1488 func_exit();
1489 return;
1490 }
Jeff Garzikd61780c02005-10-30 15:01:51 -08001491
Linus Torvalds1da177e2005-04-16 15:20:36 -07001492 bp = port_Board(port);
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001493 if (tty->count == 1 && port->port.count != 1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001494 printk(KERN_ERR "sx%d: sx_close: bad port count;"
1495 " tty->count is 1, port count is %d\n",
Alan Cox44b7d1b2008-07-16 21:57:18 +01001496 board_No(bp), port->port.count);
1497 port->port.count = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001498 }
1499
Alan Cox44b7d1b2008-07-16 21:57:18 +01001500 if (port->port.count > 1) {
1501 port->port.count--;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001502 bp->count--;
1503
1504 spin_unlock_irqrestore(&port->lock, flags);
1505
1506 func_exit();
1507 return;
1508 }
Alan Cox44b7d1b2008-07-16 21:57:18 +01001509 port->port.flags |= ASYNC_CLOSING;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001510 /*
Jeff Garzikd61780c02005-10-30 15:01:51 -08001511 * Now we wait for the transmit buffer to clear; and we notify
Linus Torvalds1da177e2005-04-16 15:20:36 -07001512 * the line discipline to only process XON/XOFF characters.
1513 */
1514 tty->closing = 1;
1515 spin_unlock_irqrestore(&port->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001516 dprintk(SX_DEBUG_OPEN, "Closing\n");
1517 if (port->port.closing_wait != ASYNC_CLOSING_WAIT_NONE)
Alan Cox44b7d1b2008-07-16 21:57:18 +01001518 tty_wait_until_sent(tty, port->port.closing_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001519 /*
1520 * At this point we stop accepting input. To do this, we
1521 * disable the receive line status interrupts, and tell the
1522 * interrupt driver to stop checking the data ready bit in the
1523 * line status register.
1524 */
Alan Coxa72492b2008-07-22 11:17:05 +01001525 dprintk(SX_DEBUG_OPEN, "Closed\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001526 port->IER &= ~IER_RXD;
Alan Cox44b7d1b2008-07-16 21:57:18 +01001527 if (port->port.flags & ASYNC_INITIALIZED) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001528 port->IER &= ~IER_TXRDY;
1529 port->IER |= IER_TXEMPTY;
1530 spin_lock_irqsave(&bp->lock, flags);
1531 sx_out(bp, CD186x_CAR, port_No(port));
1532 sx_out(bp, CD186x_IER, port->IER);
1533 spin_unlock_irqrestore(&bp->lock, flags);
1534 /*
1535 * Before we drop DTR, make sure the UART transmitter
1536 * has completely drained; this is especially
1537 * important if there is a transmit FIFO!
1538 */
1539 timeout = jiffies+HZ;
Alan Coxa72492b2008-07-22 11:17:05 +01001540 while (port->IER & IER_TXEMPTY) {
1541 set_current_state(TASK_INTERRUPTIBLE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542 msleep_interruptible(jiffies_to_msecs(port->timeout));
1543 if (time_after(jiffies, timeout)) {
Alan Coxa72492b2008-07-22 11:17:05 +01001544 printk(KERN_INFO "Timeout waiting for close\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001545 break;
1546 }
1547 }
1548
1549 }
1550
1551 if (--bp->count < 0) {
Alan Coxa72492b2008-07-22 11:17:05 +01001552 printk(KERN_ERR
1553 "sx%d: sx_shutdown_port: bad board count: %d port: %d\n",
1554 board_No(bp), bp->count, tty->index);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001555 bp->count = 0;
1556 }
Alan Cox44b7d1b2008-07-16 21:57:18 +01001557 if (--port->port.count < 0) {
Alan Coxa72492b2008-07-22 11:17:05 +01001558 printk(KERN_ERR
1559 "sx%d: sx_close: bad port count for tty%d: %d\n",
1560 board_No(bp), port_No(port), port->port.count);
Alan Cox44b7d1b2008-07-16 21:57:18 +01001561 port->port.count = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001562 }
1563
1564 sx_shutdown_port(bp, port);
Alan Cox978e5952008-04-30 00:53:59 -07001565 sx_flush_buffer(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001566 tty_ldisc_flush(tty);
1567 spin_lock_irqsave(&port->lock, flags);
1568 tty->closing = 0;
Alan Cox44b7d1b2008-07-16 21:57:18 +01001569 port->port.tty = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001570 spin_unlock_irqrestore(&port->lock, flags);
Alan Cox44b7d1b2008-07-16 21:57:18 +01001571 if (port->port.blocked_open) {
Alan Coxa72492b2008-07-22 11:17:05 +01001572 if (port->port.close_delay)
1573 msleep_interruptible(
1574 jiffies_to_msecs(port->port.close_delay));
Alan Cox44b7d1b2008-07-16 21:57:18 +01001575 wake_up_interruptible(&port->port.open_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001576 }
Alan Cox44b7d1b2008-07-16 21:57:18 +01001577 port->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
1578 wake_up_interruptible(&port->port.close_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001579
1580 func_exit();
1581}
1582
1583
Alan Coxa72492b2008-07-22 11:17:05 +01001584static int sx_write(struct tty_struct *tty,
1585 const unsigned char *buf, int count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001586{
Alan Coxc9f19e92009-01-02 13:47:26 +00001587 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001588 struct specialix_board *bp;
1589 int c, total = 0;
1590 unsigned long flags;
1591
1592 func_enter();
1593 if (sx_paranoia_check(port, tty->name, "sx_write")) {
1594 func_exit();
1595 return 0;
1596 }
Jeff Garzikd61780c02005-10-30 15:01:51 -08001597
Linus Torvalds1da177e2005-04-16 15:20:36 -07001598 bp = port_Board(port);
1599
Jiri Slaby365e0222006-09-30 23:28:11 -07001600 if (!port->xmit_buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001601 func_exit();
1602 return 0;
1603 }
1604
1605 while (1) {
1606 spin_lock_irqsave(&port->lock, flags);
1607 c = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
1608 SERIAL_XMIT_SIZE - port->xmit_head));
1609 if (c <= 0) {
1610 spin_unlock_irqrestore(&port->lock, flags);
1611 break;
1612 }
1613 memcpy(port->xmit_buf + port->xmit_head, buf, c);
1614 port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
1615 port->xmit_cnt += c;
1616 spin_unlock_irqrestore(&port->lock, flags);
1617
1618 buf += c;
1619 count -= c;
1620 total += c;
1621 }
1622
1623 spin_lock_irqsave(&bp->lock, flags);
1624 if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
1625 !(port->IER & IER_TXRDY)) {
1626 port->IER |= IER_TXRDY;
1627 sx_out(bp, CD186x_CAR, port_No(port));
1628 sx_out(bp, CD186x_IER, port->IER);
1629 }
1630 spin_unlock_irqrestore(&bp->lock, flags);
1631 func_exit();
1632
1633 return total;
1634}
1635
1636
Alan Coxa72492b2008-07-22 11:17:05 +01001637static int sx_put_char(struct tty_struct *tty, unsigned char ch)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001638{
Alan Coxc9f19e92009-01-02 13:47:26 +00001639 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001640 unsigned long flags;
Alan Coxa72492b2008-07-22 11:17:05 +01001641 struct specialix_board *bp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001642
1643 func_enter();
1644
1645 if (sx_paranoia_check(port, tty->name, "sx_put_char")) {
1646 func_exit();
Alan Cox6ae045762008-04-30 00:54:07 -07001647 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001648 }
Alan Coxa72492b2008-07-22 11:17:05 +01001649 dprintk(SX_DEBUG_TX, "check tty: %p %p\n", tty, port->xmit_buf);
Eric Sesterhenn326f28e92006-06-25 05:48:48 -07001650 if (!port->xmit_buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001651 func_exit();
Alan Cox6ae045762008-04-30 00:54:07 -07001652 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001653 }
1654 bp = port_Board(port);
1655 spin_lock_irqsave(&port->lock, flags);
1656
Alan Coxa72492b2008-07-22 11:17:05 +01001657 dprintk(SX_DEBUG_TX, "xmit_cnt: %d xmit_buf: %p\n",
1658 port->xmit_cnt, port->xmit_buf);
1659 if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1 || !port->xmit_buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001660 spin_unlock_irqrestore(&port->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001661 dprintk(SX_DEBUG_TX, "Exit size\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001662 func_exit();
Alan Cox6ae045762008-04-30 00:54:07 -07001663 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001664 }
Alan Coxa72492b2008-07-22 11:17:05 +01001665 dprintk(SX_DEBUG_TX, "Handle xmit: %p %p\n", port, port->xmit_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001666 port->xmit_buf[port->xmit_head++] = ch;
1667 port->xmit_head &= SERIAL_XMIT_SIZE - 1;
1668 port->xmit_cnt++;
1669 spin_unlock_irqrestore(&port->lock, flags);
1670
1671 func_exit();
Alan Cox6ae045762008-04-30 00:54:07 -07001672 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001673}
1674
1675
Alan Coxa72492b2008-07-22 11:17:05 +01001676static void sx_flush_chars(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001677{
Alan Coxc9f19e92009-01-02 13:47:26 +00001678 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001679 unsigned long flags;
Alan Coxa72492b2008-07-22 11:17:05 +01001680 struct specialix_board *bp = port_Board(port);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001681
1682 func_enter();
1683
1684 if (sx_paranoia_check(port, tty->name, "sx_flush_chars")) {
1685 func_exit();
1686 return;
1687 }
1688 if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
1689 !port->xmit_buf) {
1690 func_exit();
1691 return;
1692 }
1693 spin_lock_irqsave(&bp->lock, flags);
1694 port->IER |= IER_TXRDY;
1695 sx_out(port_Board(port), CD186x_CAR, port_No(port));
1696 sx_out(port_Board(port), CD186x_IER, port->IER);
1697 spin_unlock_irqrestore(&bp->lock, flags);
1698
1699 func_exit();
1700}
1701
1702
Alan Coxa72492b2008-07-22 11:17:05 +01001703static int sx_write_room(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001704{
Alan Coxc9f19e92009-01-02 13:47:26 +00001705 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001706 int ret;
1707
1708 func_enter();
1709
1710 if (sx_paranoia_check(port, tty->name, "sx_write_room")) {
1711 func_exit();
1712 return 0;
1713 }
1714
1715 ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;
1716 if (ret < 0)
1717 ret = 0;
1718
1719 func_exit();
1720 return ret;
1721}
1722
1723
1724static int sx_chars_in_buffer(struct tty_struct *tty)
1725{
Alan Coxc9f19e92009-01-02 13:47:26 +00001726 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001727
1728 func_enter();
Jeff Garzikd61780c02005-10-30 15:01:51 -08001729
Linus Torvalds1da177e2005-04-16 15:20:36 -07001730 if (sx_paranoia_check(port, tty->name, "sx_chars_in_buffer")) {
1731 func_exit();
1732 return 0;
1733 }
1734 func_exit();
1735 return port->xmit_cnt;
1736}
1737
Linus Torvalds1da177e2005-04-16 15:20:36 -07001738static int sx_tiocmget(struct tty_struct *tty, struct file *file)
1739{
Alan Coxc9f19e92009-01-02 13:47:26 +00001740 struct specialix_port *port = tty->driver_data;
Alan Coxa72492b2008-07-22 11:17:05 +01001741 struct specialix_board *bp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001742 unsigned char status;
1743 unsigned int result;
1744 unsigned long flags;
1745
1746 func_enter();
1747
Harvey Harrisonbf9d8922008-04-30 00:55:10 -07001748 if (sx_paranoia_check(port, tty->name, __func__)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001749 func_exit();
1750 return -ENODEV;
1751 }
1752
1753 bp = port_Board(port);
Alan Coxa72492b2008-07-22 11:17:05 +01001754 spin_lock_irqsave(&bp->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001755 sx_out(bp, CD186x_CAR, port_No(port));
1756 status = sx_in(bp, CD186x_MSVR);
1757 spin_unlock_irqrestore(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001758 dprintk(SX_DEBUG_INIT, "Got msvr[%d] = %02x, car = %d.\n",
1759 port_No(port), status, sx_in(bp, CD186x_CAR));
1760 dprintk(SX_DEBUG_INIT, "sx_port = %p, port = %p\n", sx_port, port);
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001761 if (sx_crtscts(port->port.tty)) {
1762 result = TIOCM_DTR | TIOCM_DSR
Alan Coxa72492b2008-07-22 11:17:05 +01001763 | ((status & MSVR_DTR) ? TIOCM_RTS : 0)
1764 | ((status & MSVR_CD) ? TIOCM_CAR : 0)
Alan Coxa72492b2008-07-22 11:17:05 +01001765 | ((status & MSVR_CTS) ? TIOCM_CTS : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001766 } else {
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001767 result = TIOCM_RTS | TIOCM_DSR
Alan Coxa72492b2008-07-22 11:17:05 +01001768 | ((status & MSVR_DTR) ? TIOCM_DTR : 0)
1769 | ((status & MSVR_CD) ? TIOCM_CAR : 0)
Alan Coxa72492b2008-07-22 11:17:05 +01001770 | ((status & MSVR_CTS) ? TIOCM_CTS : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001771 }
1772
1773 func_exit();
1774
1775 return result;
1776}
1777
1778
1779static int sx_tiocmset(struct tty_struct *tty, struct file *file,
1780 unsigned int set, unsigned int clear)
1781{
Alan Coxc9f19e92009-01-02 13:47:26 +00001782 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001783 unsigned long flags;
1784 struct specialix_board *bp;
1785
1786 func_enter();
1787
Harvey Harrisonbf9d8922008-04-30 00:55:10 -07001788 if (sx_paranoia_check(port, tty->name, __func__)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001789 func_exit();
1790 return -ENODEV;
1791 }
1792
1793 bp = port_Board(port);
1794
1795 spin_lock_irqsave(&port->lock, flags);
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001796 if (sx_crtscts(port->port.tty)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001797 if (set & TIOCM_RTS)
1798 port->MSVR |= MSVR_DTR;
1799 } else {
1800 if (set & TIOCM_DTR)
1801 port->MSVR |= MSVR_DTR;
1802 }
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001803 if (sx_crtscts(port->port.tty)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001804 if (clear & TIOCM_RTS)
1805 port->MSVR &= ~MSVR_DTR;
1806 } else {
1807 if (clear & TIOCM_DTR)
1808 port->MSVR &= ~MSVR_DTR;
1809 }
Julia Lawall25470252009-07-20 16:05:07 +01001810 spin_lock(&bp->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001811 sx_out(bp, CD186x_CAR, port_No(port));
1812 sx_out(bp, CD186x_MSVR, port->MSVR);
Julia Lawall25470252009-07-20 16:05:07 +01001813 spin_unlock(&bp->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001814 spin_unlock_irqrestore(&port->lock, flags);
1815 func_exit();
1816 return 0;
1817}
1818
1819
Alan Coxfaa76122008-07-22 11:19:05 +01001820static int sx_send_break(struct tty_struct *tty, int length)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001821{
Alan Coxc9f19e92009-01-02 13:47:26 +00001822 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001823 struct specialix_board *bp = port_Board(port);
1824 unsigned long flags;
Jeff Garzikd61780c02005-10-30 15:01:51 -08001825
Linus Torvalds1da177e2005-04-16 15:20:36 -07001826 func_enter();
Alan Coxfaa76122008-07-22 11:19:05 +01001827 if (length == 0 || length == -1)
1828 return -EOPNOTSUPP;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001829
Alan Coxa72492b2008-07-22 11:17:05 +01001830 spin_lock_irqsave(&port->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001831 port->break_length = SPECIALIX_TPS / HZ * length;
1832 port->COR2 |= COR2_ETC;
1833 port->IER |= IER_TXRDY;
Julia Lawall25470252009-07-20 16:05:07 +01001834 spin_lock(&bp->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001835 sx_out(bp, CD186x_CAR, port_No(port));
1836 sx_out(bp, CD186x_COR2, port->COR2);
1837 sx_out(bp, CD186x_IER, port->IER);
Julia Lawall25470252009-07-20 16:05:07 +01001838 spin_unlock(&bp->lock);
Alan Coxa72492b2008-07-22 11:17:05 +01001839 spin_unlock_irqrestore(&port->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001840 sx_wait_CCR(bp);
1841 spin_lock_irqsave(&bp->lock, flags);
1842 sx_out(bp, CD186x_CCR, CCR_CORCHG2);
1843 spin_unlock_irqrestore(&bp->lock, flags);
1844 sx_wait_CCR(bp);
1845
1846 func_exit();
Alan Coxfaa76122008-07-22 11:19:05 +01001847 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001848}
1849
1850
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001851static int sx_set_serial_info(struct specialix_port *port,
Alan Coxa72492b2008-07-22 11:17:05 +01001852 struct serial_struct __user *newinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001853{
1854 struct serial_struct tmp;
1855 struct specialix_board *bp = port_Board(port);
1856 int change_speed;
1857
1858 func_enter();
Alan Coxb190e172008-04-30 00:53:22 -07001859
Linus Torvalds1da177e2005-04-16 15:20:36 -07001860 if (copy_from_user(&tmp, newinfo, sizeof(tmp))) {
1861 func_enter();
1862 return -EFAULT;
1863 }
Jeff Garzikd61780c02005-10-30 15:01:51 -08001864
Alan Coxb190e172008-04-30 00:53:22 -07001865 lock_kernel();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001866
Alan Cox44b7d1b2008-07-16 21:57:18 +01001867 change_speed = ((port->port.flags & ASYNC_SPD_MASK) !=
Linus Torvalds1da177e2005-04-16 15:20:36 -07001868 (tmp.flags & ASYNC_SPD_MASK));
1869 change_speed |= (tmp.custom_divisor != port->custom_divisor);
Jeff Garzikd61780c02005-10-30 15:01:51 -08001870
Linus Torvalds1da177e2005-04-16 15:20:36 -07001871 if (!capable(CAP_SYS_ADMIN)) {
Alan Cox44b7d1b2008-07-16 21:57:18 +01001872 if ((tmp.close_delay != port->port.close_delay) ||
1873 (tmp.closing_wait != port->port.closing_wait) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001874 ((tmp.flags & ~ASYNC_USR_MASK) !=
Alan Cox44b7d1b2008-07-16 21:57:18 +01001875 (port->port.flags & ~ASYNC_USR_MASK))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001876 func_exit();
Alan Coxb190e172008-04-30 00:53:22 -07001877 unlock_kernel();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001878 return -EPERM;
1879 }
Alan Cox44b7d1b2008-07-16 21:57:18 +01001880 port->port.flags = ((port->port.flags & ~ASYNC_USR_MASK) |
Alan Coxa72492b2008-07-22 11:17:05 +01001881 (tmp.flags & ASYNC_USR_MASK));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001882 port->custom_divisor = tmp.custom_divisor;
1883 } else {
Alan Cox44b7d1b2008-07-16 21:57:18 +01001884 port->port.flags = ((port->port.flags & ~ASYNC_FLAGS) |
Alan Coxa72492b2008-07-22 11:17:05 +01001885 (tmp.flags & ASYNC_FLAGS));
Alan Cox44b7d1b2008-07-16 21:57:18 +01001886 port->port.close_delay = tmp.close_delay;
1887 port->port.closing_wait = tmp.closing_wait;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001888 port->custom_divisor = tmp.custom_divisor;
1889 }
Alan Coxa72492b2008-07-22 11:17:05 +01001890 if (change_speed)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001891 sx_change_speed(bp, port);
Alan Coxa72492b2008-07-22 11:17:05 +01001892
Linus Torvalds1da177e2005-04-16 15:20:36 -07001893 func_exit();
Alan Coxb190e172008-04-30 00:53:22 -07001894 unlock_kernel();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001895 return 0;
1896}
1897
1898
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001899static int sx_get_serial_info(struct specialix_port *port,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001900 struct serial_struct __user *retinfo)
1901{
1902 struct serial_struct tmp;
1903 struct specialix_board *bp = port_Board(port);
Jeff Garzikd61780c02005-10-30 15:01:51 -08001904
Linus Torvalds1da177e2005-04-16 15:20:36 -07001905 func_enter();
1906
Linus Torvalds1da177e2005-04-16 15:20:36 -07001907 memset(&tmp, 0, sizeof(tmp));
Alan Coxb190e172008-04-30 00:53:22 -07001908 lock_kernel();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001909 tmp.type = PORT_CIRRUS;
1910 tmp.line = port - sx_port;
1911 tmp.port = bp->base;
1912 tmp.irq = bp->irq;
Alan Cox44b7d1b2008-07-16 21:57:18 +01001913 tmp.flags = port->port.flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001914 tmp.baud_base = (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC;
Alan Cox44b7d1b2008-07-16 21:57:18 +01001915 tmp.close_delay = port->port.close_delay * HZ/100;
1916 tmp.closing_wait = port->port.closing_wait * HZ/100;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001917 tmp.custom_divisor = port->custom_divisor;
1918 tmp.xmit_fifo_size = CD186x_NFIFO;
Alan Coxb190e172008-04-30 00:53:22 -07001919 unlock_kernel();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001920 if (copy_to_user(retinfo, &tmp, sizeof(tmp))) {
1921 func_exit();
1922 return -EFAULT;
1923 }
1924
1925 func_exit();
1926 return 0;
1927}
1928
1929
Alan Coxa72492b2008-07-22 11:17:05 +01001930static int sx_ioctl(struct tty_struct *tty, struct file *filp,
1931 unsigned int cmd, unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001932{
Alan Coxc9f19e92009-01-02 13:47:26 +00001933 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001934 void __user *argp = (void __user *)arg;
1935
1936 func_enter();
1937
1938 if (sx_paranoia_check(port, tty->name, "sx_ioctl")) {
1939 func_exit();
1940 return -ENODEV;
1941 }
Jeff Garzikd61780c02005-10-30 15:01:51 -08001942
Linus Torvalds1da177e2005-04-16 15:20:36 -07001943 switch (cmd) {
Alan Coxa72492b2008-07-22 11:17:05 +01001944 case TIOCGSERIAL:
Alan Coxfaa76122008-07-22 11:19:05 +01001945 func_exit();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001946 return sx_get_serial_info(port, argp);
Alan Coxa72492b2008-07-22 11:17:05 +01001947 case TIOCSSERIAL:
Alan Coxfaa76122008-07-22 11:19:05 +01001948 func_exit();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001949 return sx_set_serial_info(port, argp);
Alan Coxa72492b2008-07-22 11:17:05 +01001950 default:
Alan Coxfaa76122008-07-22 11:19:05 +01001951 func_exit();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001952 return -ENOIOCTLCMD;
1953 }
1954 func_exit();
1955 return 0;
1956}
1957
1958
Alan Coxa72492b2008-07-22 11:17:05 +01001959static void sx_throttle(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001960{
Alan Coxc9f19e92009-01-02 13:47:26 +00001961 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001962 struct specialix_board *bp;
1963 unsigned long flags;
1964
1965 func_enter();
1966
1967 if (sx_paranoia_check(port, tty->name, "sx_throttle")) {
1968 func_exit();
1969 return;
1970 }
Jeff Garzikd61780c02005-10-30 15:01:51 -08001971
Linus Torvalds1da177e2005-04-16 15:20:36 -07001972 bp = port_Board(port);
Jeff Garzikd61780c02005-10-30 15:01:51 -08001973
Linus Torvalds1da177e2005-04-16 15:20:36 -07001974 /* Use DTR instead of RTS ! */
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001975 if (sx_crtscts(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001976 port->MSVR &= ~MSVR_DTR;
1977 else {
1978 /* Auch!!! I think the system shouldn't call this then. */
1979 /* Or maybe we're supposed (allowed?) to do our side of hw
Jeff Garzikd61780c02005-10-30 15:01:51 -08001980 handshake anyway, even when hardware handshake is off.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001981 When you see this in your logs, please report.... */
Alan Coxa72492b2008-07-22 11:17:05 +01001982 printk(KERN_ERR
1983 "sx%d: Need to throttle, but can't (hardware hs is off)\n",
1984 port_No(port));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001985 }
1986 spin_lock_irqsave(&bp->lock, flags);
1987 sx_out(bp, CD186x_CAR, port_No(port));
1988 spin_unlock_irqrestore(&bp->lock, flags);
1989 if (I_IXOFF(tty)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001990 sx_wait_CCR(bp);
1991 spin_lock_irqsave(&bp->lock, flags);
1992 sx_out(bp, CD186x_CCR, CCR_SSCH2);
1993 spin_unlock_irqrestore(&bp->lock, flags);
1994 sx_wait_CCR(bp);
1995 }
1996 spin_lock_irqsave(&bp->lock, flags);
1997 sx_out(bp, CD186x_MSVR, port->MSVR);
1998 spin_unlock_irqrestore(&bp->lock, flags);
1999
2000 func_exit();
2001}
2002
2003
Alan Coxa72492b2008-07-22 11:17:05 +01002004static void sx_unthrottle(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002005{
Alan Coxc9f19e92009-01-02 13:47:26 +00002006 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002007 struct specialix_board *bp;
2008 unsigned long flags;
2009
2010 func_enter();
Jeff Garzikd61780c02005-10-30 15:01:51 -08002011
Linus Torvalds1da177e2005-04-16 15:20:36 -07002012 if (sx_paranoia_check(port, tty->name, "sx_unthrottle")) {
2013 func_exit();
2014 return;
2015 }
Jeff Garzikd61780c02005-10-30 15:01:51 -08002016
Linus Torvalds1da177e2005-04-16 15:20:36 -07002017 bp = port_Board(port);
Jeff Garzikd61780c02005-10-30 15:01:51 -08002018
Linus Torvalds1da177e2005-04-16 15:20:36 -07002019 spin_lock_irqsave(&port->lock, flags);
2020 /* XXXX Use DTR INSTEAD???? */
Alan Coxd2fbd0f2008-07-22 11:17:16 +01002021 if (sx_crtscts(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002022 port->MSVR |= MSVR_DTR;
Alan Coxa72492b2008-07-22 11:17:05 +01002023 /* Else clause: see remark in "sx_throttle"... */
Julia Lawall25470252009-07-20 16:05:07 +01002024 spin_lock(&bp->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002025 sx_out(bp, CD186x_CAR, port_No(port));
Julia Lawall25470252009-07-20 16:05:07 +01002026 spin_unlock(&bp->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002027 if (I_IXOFF(tty)) {
2028 spin_unlock_irqrestore(&port->lock, flags);
2029 sx_wait_CCR(bp);
2030 spin_lock_irqsave(&bp->lock, flags);
2031 sx_out(bp, CD186x_CCR, CCR_SSCH1);
2032 spin_unlock_irqrestore(&bp->lock, flags);
2033 sx_wait_CCR(bp);
2034 spin_lock_irqsave(&port->lock, flags);
2035 }
Julia Lawall25470252009-07-20 16:05:07 +01002036 spin_lock(&bp->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002037 sx_out(bp, CD186x_MSVR, port->MSVR);
Julia Lawall25470252009-07-20 16:05:07 +01002038 spin_unlock(&bp->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002039 spin_unlock_irqrestore(&port->lock, flags);
2040
2041 func_exit();
2042}
2043
2044
Alan Coxa72492b2008-07-22 11:17:05 +01002045static void sx_stop(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002046{
Alan Coxc9f19e92009-01-02 13:47:26 +00002047 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002048 struct specialix_board *bp;
2049 unsigned long flags;
2050
2051 func_enter();
Jeff Garzikd61780c02005-10-30 15:01:51 -08002052
Linus Torvalds1da177e2005-04-16 15:20:36 -07002053 if (sx_paranoia_check(port, tty->name, "sx_stop")) {
2054 func_exit();
2055 return;
2056 }
2057
2058 bp = port_Board(port);
Jeff Garzikd61780c02005-10-30 15:01:51 -08002059
Linus Torvalds1da177e2005-04-16 15:20:36 -07002060 spin_lock_irqsave(&port->lock, flags);
2061 port->IER &= ~IER_TXRDY;
Julia Lawall25470252009-07-20 16:05:07 +01002062 spin_lock(&bp->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002063 sx_out(bp, CD186x_CAR, port_No(port));
2064 sx_out(bp, CD186x_IER, port->IER);
Julia Lawall25470252009-07-20 16:05:07 +01002065 spin_unlock(&bp->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002066 spin_unlock_irqrestore(&port->lock, flags);
2067
2068 func_exit();
2069}
2070
2071
Alan Coxa72492b2008-07-22 11:17:05 +01002072static void sx_start(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002073{
Alan Coxc9f19e92009-01-02 13:47:26 +00002074 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002075 struct specialix_board *bp;
2076 unsigned long flags;
2077
2078 func_enter();
Jeff Garzikd61780c02005-10-30 15:01:51 -08002079
Linus Torvalds1da177e2005-04-16 15:20:36 -07002080 if (sx_paranoia_check(port, tty->name, "sx_start")) {
2081 func_exit();
2082 return;
2083 }
Jeff Garzikd61780c02005-10-30 15:01:51 -08002084
Linus Torvalds1da177e2005-04-16 15:20:36 -07002085 bp = port_Board(port);
Jeff Garzikd61780c02005-10-30 15:01:51 -08002086
Linus Torvalds1da177e2005-04-16 15:20:36 -07002087 spin_lock_irqsave(&port->lock, flags);
2088 if (port->xmit_cnt && port->xmit_buf && !(port->IER & IER_TXRDY)) {
2089 port->IER |= IER_TXRDY;
Julia Lawall25470252009-07-20 16:05:07 +01002090 spin_lock(&bp->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002091 sx_out(bp, CD186x_CAR, port_No(port));
2092 sx_out(bp, CD186x_IER, port->IER);
Julia Lawall25470252009-07-20 16:05:07 +01002093 spin_unlock(&bp->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002094 }
2095 spin_unlock_irqrestore(&port->lock, flags);
2096
2097 func_exit();
2098}
2099
Alan Coxa72492b2008-07-22 11:17:05 +01002100static void sx_hangup(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002101{
Alan Coxc9f19e92009-01-02 13:47:26 +00002102 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002103 struct specialix_board *bp;
2104 unsigned long flags;
2105
2106 func_enter();
2107
2108 if (sx_paranoia_check(port, tty->name, "sx_hangup")) {
2109 func_exit();
2110 return;
2111 }
Jeff Garzikd61780c02005-10-30 15:01:51 -08002112
Linus Torvalds1da177e2005-04-16 15:20:36 -07002113 bp = port_Board(port);
Jeff Garzikd61780c02005-10-30 15:01:51 -08002114
Linus Torvalds1da177e2005-04-16 15:20:36 -07002115 sx_shutdown_port(bp, port);
2116 spin_lock_irqsave(&port->lock, flags);
Alan Cox44b7d1b2008-07-16 21:57:18 +01002117 bp->count -= port->port.count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002118 if (bp->count < 0) {
Alan Coxa72492b2008-07-22 11:17:05 +01002119 printk(KERN_ERR
2120 "sx%d: sx_hangup: bad board count: %d port: %d\n",
2121 board_No(bp), bp->count, tty->index);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002122 bp->count = 0;
2123 }
Alan Cox44b7d1b2008-07-16 21:57:18 +01002124 port->port.count = 0;
2125 port->port.flags &= ~ASYNC_NORMAL_ACTIVE;
2126 port->port.tty = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002127 spin_unlock_irqrestore(&port->lock, flags);
Alan Cox44b7d1b2008-07-16 21:57:18 +01002128 wake_up_interruptible(&port->port.open_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002129
2130 func_exit();
2131}
2132
2133
Alan Coxa72492b2008-07-22 11:17:05 +01002134static void sx_set_termios(struct tty_struct *tty,
2135 struct ktermios *old_termios)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002136{
Alan Coxc9f19e92009-01-02 13:47:26 +00002137 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002138 unsigned long flags;
Alan Coxa72492b2008-07-22 11:17:05 +01002139 struct specialix_board *bp;
Jeff Garzikd61780c02005-10-30 15:01:51 -08002140
Linus Torvalds1da177e2005-04-16 15:20:36 -07002141 if (sx_paranoia_check(port, tty->name, "sx_set_termios"))
2142 return;
Jeff Garzikd61780c02005-10-30 15:01:51 -08002143
Linus Torvalds1da177e2005-04-16 15:20:36 -07002144 bp = port_Board(port);
2145 spin_lock_irqsave(&port->lock, flags);
2146 sx_change_speed(port_Board(port), port);
2147 spin_unlock_irqrestore(&port->lock, flags);
2148
2149 if ((old_termios->c_cflag & CRTSCTS) &&
2150 !(tty->termios->c_cflag & CRTSCTS)) {
2151 tty->hw_stopped = 0;
2152 sx_start(tty);
2153 }
2154}
2155
Jeff Dikeb68e31d2006-10-02 02:17:18 -07002156static const struct tty_operations sx_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002157 .open = sx_open,
2158 .close = sx_close,
2159 .write = sx_write,
2160 .put_char = sx_put_char,
2161 .flush_chars = sx_flush_chars,
2162 .write_room = sx_write_room,
2163 .chars_in_buffer = sx_chars_in_buffer,
2164 .flush_buffer = sx_flush_buffer,
2165 .ioctl = sx_ioctl,
2166 .throttle = sx_throttle,
2167 .unthrottle = sx_unthrottle,
2168 .set_termios = sx_set_termios,
2169 .stop = sx_stop,
2170 .start = sx_start,
2171 .hangup = sx_hangup,
2172 .tiocmget = sx_tiocmget,
2173 .tiocmset = sx_tiocmset,
Alan Coxfaa76122008-07-22 11:19:05 +01002174 .break_ctl = sx_send_break,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002175};
2176
2177static int sx_init_drivers(void)
2178{
2179 int error;
2180 int i;
2181
2182 func_enter();
2183
2184 specialix_driver = alloc_tty_driver(SX_NBOARD * SX_NPORT);
2185 if (!specialix_driver) {
2186 printk(KERN_ERR "sx: Couldn't allocate tty_driver.\n");
2187 func_exit();
2188 return 1;
2189 }
Jeff Garzikd61780c02005-10-30 15:01:51 -08002190
Linus Torvalds1da177e2005-04-16 15:20:36 -07002191 specialix_driver->owner = THIS_MODULE;
2192 specialix_driver->name = "ttyW";
2193 specialix_driver->major = SPECIALIX_NORMAL_MAJOR;
2194 specialix_driver->type = TTY_DRIVER_TYPE_SERIAL;
2195 specialix_driver->subtype = SERIAL_TYPE_NORMAL;
2196 specialix_driver->init_termios = tty_std_termios;
2197 specialix_driver->init_termios.c_cflag =
2198 B9600 | CS8 | CREAD | HUPCL | CLOCAL;
Alan Cox606d0992006-12-08 02:38:45 -08002199 specialix_driver->init_termios.c_ispeed = 9600;
2200 specialix_driver->init_termios.c_ospeed = 9600;
Alan Coxfaa76122008-07-22 11:19:05 +01002201 specialix_driver->flags = TTY_DRIVER_REAL_RAW |
2202 TTY_DRIVER_HARDWARE_BREAK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002203 tty_set_operations(specialix_driver, &sx_ops);
2204
Alan Coxa72492b2008-07-22 11:17:05 +01002205 error = tty_register_driver(specialix_driver);
2206 if (error) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002207 put_tty_driver(specialix_driver);
Alan Coxa72492b2008-07-22 11:17:05 +01002208 printk(KERN_ERR
2209 "sx: Couldn't register specialix IO8+ driver, error = %d\n",
2210 error);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002211 func_exit();
2212 return 1;
2213 }
2214 memset(sx_port, 0, sizeof(sx_port));
2215 for (i = 0; i < SX_NPORT * SX_NBOARD; i++) {
2216 sx_port[i].magic = SPECIALIX_MAGIC;
Alan Cox44b7d1b2008-07-16 21:57:18 +01002217 tty_port_init(&sx_port[i].port);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002218 spin_lock_init(&sx_port[i].lock);
2219 }
Jeff Garzikd61780c02005-10-30 15:01:51 -08002220
Linus Torvalds1da177e2005-04-16 15:20:36 -07002221 func_exit();
2222 return 0;
2223}
2224
2225static void sx_release_drivers(void)
2226{
2227 func_enter();
2228
Linus Torvalds1da177e2005-04-16 15:20:36 -07002229 tty_unregister_driver(specialix_driver);
2230 put_tty_driver(specialix_driver);
2231 func_exit();
2232}
2233
Jeff Garzikd61780c02005-10-30 15:01:51 -08002234/*
2235 * This routine must be called by kernel at boot time
Linus Torvalds1da177e2005-04-16 15:20:36 -07002236 */
2237static int __init specialix_init(void)
2238{
2239 int i;
2240 int found = 0;
2241
2242 func_enter();
2243
2244 printk(KERN_INFO "sx: Specialix IO8+ driver v" VERSION ", (c) R.E.Wolff 1997/1998.\n");
2245 printk(KERN_INFO "sx: derived from work (c) D.Gorodchanin 1994-1996.\n");
Alan Coxd2fbd0f2008-07-22 11:17:16 +01002246 if (sx_rtscts)
2247 printk(KERN_INFO
2248 "sx: DTR/RTS pin is RTS when CRTSCTS is on.\n");
2249 else
2250 printk(KERN_INFO "sx: DTR/RTS pin is always RTS.\n");
Jeff Garzikd61780c02005-10-30 15:01:51 -08002251
Linus Torvalds1da177e2005-04-16 15:20:36 -07002252 for (i = 0; i < SX_NBOARD; i++)
Ingo Molnar34af9462006-06-27 02:53:55 -07002253 spin_lock_init(&sx_board[i].lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002254
2255 if (sx_init_drivers()) {
2256 func_exit();
2257 return -EIO;
2258 }
2259
Jeff Garzikd61780c02005-10-30 15:01:51 -08002260 for (i = 0; i < SX_NBOARD; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002261 if (sx_board[i].base && !sx_probe(&sx_board[i]))
2262 found++;
2263
2264#ifdef CONFIG_PCI
2265 {
2266 struct pci_dev *pdev = NULL;
2267
Alan Coxa72492b2008-07-22 11:17:05 +01002268 i = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002269 while (i < SX_NBOARD) {
2270 if (sx_board[i].flags & SX_BOARD_PRESENT) {
2271 i++;
2272 continue;
2273 }
Alan Coxa72492b2008-07-22 11:17:05 +01002274 pdev = pci_get_device(PCI_VENDOR_ID_SPECIALIX,
2275 PCI_DEVICE_ID_SPECIALIX_IO8, pdev);
2276 if (!pdev)
2277 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002278
2279 if (pci_enable_device(pdev))
2280 continue;
2281
2282 sx_board[i].irq = pdev->irq;
2283
Alan Coxa72492b2008-07-22 11:17:05 +01002284 sx_board[i].base = pci_resource_start(pdev, 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002285
2286 sx_board[i].flags |= SX_BOARD_IS_PCI;
2287 if (!sx_probe(&sx_board[i]))
Alan Coxa72492b2008-07-22 11:17:05 +01002288 found++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002289 }
Alan Cox606d0992006-12-08 02:38:45 -08002290 /* May exit pci_get sequence early with lots of boards */
2291 if (pdev != NULL)
2292 pci_dev_put(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002293 }
2294#endif
2295
2296 if (!found) {
2297 sx_release_drivers();
2298 printk(KERN_INFO "sx: No specialix IO8+ boards detected.\n");
2299 func_exit();
2300 return -EIO;
2301 }
2302
2303 func_exit();
2304 return 0;
2305}
2306
2307static int iobase[SX_NBOARD] = {0,};
Alan Coxd2fbd0f2008-07-22 11:17:16 +01002308static int irq[SX_NBOARD] = {0,};
Linus Torvalds1da177e2005-04-16 15:20:36 -07002309
2310module_param_array(iobase, int, NULL, 0);
2311module_param_array(irq, int, NULL, 0);
2312module_param(sx_debug, int, 0);
Alan Coxd2fbd0f2008-07-22 11:17:16 +01002313module_param(sx_rtscts, int, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002314module_param(sx_rxfifo, int, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002315
2316/*
2317 * You can setup up to 4 boards.
2318 * by specifying "iobase=0xXXX,0xXXX ..." as insmod parameter.
Jeff Garzikd61780c02005-10-30 15:01:51 -08002319 * You should specify the IRQs too in that case "irq=....,...".
2320 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07002321 * More than 4 boards in one computer is not possible, as the card can
Jeff Garzikd61780c02005-10-30 15:01:51 -08002322 * only use 4 different interrupts.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002323 *
2324 */
2325static int __init specialix_init_module(void)
2326{
2327 int i;
2328
2329 func_enter();
2330
Linus Torvalds1da177e2005-04-16 15:20:36 -07002331 if (iobase[0] || iobase[1] || iobase[2] || iobase[3]) {
Alan Coxa72492b2008-07-22 11:17:05 +01002332 for (i = 0; i < SX_NBOARD; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002333 sx_board[i].base = iobase[i];
2334 sx_board[i].irq = irq[i];
Alan Coxa72492b2008-07-22 11:17:05 +01002335 sx_board[i].count = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002336 }
2337 }
2338
2339 func_exit();
2340
2341 return specialix_init();
2342}
Jeff Garzikd61780c02005-10-30 15:01:51 -08002343
Linus Torvalds1da177e2005-04-16 15:20:36 -07002344static void __exit specialix_exit_module(void)
2345{
2346 int i;
Jeff Garzikd61780c02005-10-30 15:01:51 -08002347
Linus Torvalds1da177e2005-04-16 15:20:36 -07002348 func_enter();
2349
2350 sx_release_drivers();
2351 for (i = 0; i < SX_NBOARD; i++)
Jeff Garzikd61780c02005-10-30 15:01:51 -08002352 if (sx_board[i].flags & SX_BOARD_PRESENT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002353 sx_release_io_range(&sx_board[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002354 func_exit();
2355}
2356
Chuck Short76910302006-07-10 04:43:59 -07002357static struct pci_device_id specialx_pci_tbl[] __devinitdata = {
2358 { PCI_DEVICE(PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_SPECIALIX_IO8) },
2359 { }
2360};
2361MODULE_DEVICE_TABLE(pci, specialx_pci_tbl);
2362
Linus Torvalds1da177e2005-04-16 15:20:36 -07002363module_init(specialix_init_module);
2364module_exit(specialix_exit_module);
2365
2366MODULE_LICENSE("GPL");
Scott James Remnant5350d3b2009-04-06 17:33:11 +01002367MODULE_ALIAS_CHARDEV_MAJOR(SPECIALIX_NORMAL_MAJOR);