blob: 268e17f9ec3f50c4586607bb0dd59e76442f3bee [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * specialix.c -- specialix IO8+ multiport serial driver.
3 *
4 * Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl)
5 * Copyright (C) 1994-1996 Dmitry Gorodchanin (pgmdsg@ibi.com)
6 *
7 * Specialix pays for the development and support of this driver.
8 * Please DO contact io8-linux@specialix.co.uk if you require
9 * support. But please read the documentation (specialix.txt)
10 * first.
11 *
12 * This driver was developped in the BitWizard linux device
13 * driver service. If you require a linux device driver for your
14 * product, please contact devices@BitWizard.nl for a quote.
15 *
16 * This code is firmly based on the riscom/8 serial driver,
17 * written by Dmitry Gorodchanin. The specialix IO8+ card
18 * programming information was obtained from the CL-CD1865 Data
19 * Book, and Specialix document number 6200059: IO8+ Hardware
20 * Functional Specification.
21 *
22 * This program is free software; you can redistribute it and/or
23 * modify it under the terms of the GNU General Public License as
24 * published by the Free Software Foundation; either version 2 of
25 * the License, or (at your option) any later version.
26 *
27 * This program is distributed in the hope that it will be
28 * useful, but WITHOUT ANY WARRANTY; without even the implied
29 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
30 * PURPOSE. See the GNU General Public License for more details.
31 *
32 * You should have received a copy of the GNU General Public
33 * License along with this program; if not, write to the Free
34 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
35 * USA.
36 *
37 * Revision history:
38 *
39 * Revision 1.0: April 1st 1997.
40 * Initial release for alpha testing.
Jeff Garzikd61780c2005-10-30 15:01:51 -080041 * Revision 1.1: April 14th 1997.
42 * Incorporated Richard Hudsons suggestions,
Linus Torvalds1da177e2005-04-16 15:20:36 -070043 * removed some debugging printk's.
44 * Revision 1.2: April 15th 1997.
45 * Ported to 2.1.x kernels.
Jeff Garzikd61780c2005-10-30 15:01:51 -080046 * Revision 1.3: April 17th 1997
47 * Backported to 2.0. (Compatibility macros).
Linus Torvalds1da177e2005-04-16 15:20:36 -070048 * Revision 1.4: April 18th 1997
Jeff Garzikd61780c2005-10-30 15:01:51 -080049 * Fixed DTR/RTS bug that caused the card to indicate
50 * "don't send data" to a modem after the password prompt.
Linus Torvalds1da177e2005-04-16 15:20:36 -070051 * Fixed bug for premature (fake) interrupts.
52 * Revision 1.5: April 19th 1997
Jeff Garzikd61780c2005-10-30 15:01:51 -080053 * fixed a minor typo in the header file, cleanup a little.
Linus Torvalds1da177e2005-04-16 15:20:36 -070054 * performance warnings are now MAXed at once per minute.
55 * Revision 1.6: May 23 1997
56 * Changed the specialix=... format to include interrupt.
57 * Revision 1.7: May 27 1997
58 * Made many more debug printk's a compile time option.
59 * Revision 1.8: Jul 1 1997
60 * port to linux-2.1.43 kernel.
61 * Revision 1.9: Oct 9 1998
62 * Added stuff for the IO8+/PCI version.
Jeff Garzikd61780c2005-10-30 15:01:51 -080063 * Revision 1.10: Oct 22 1999 / Jan 21 2000.
64 * Added stuff for setserial.
Linus Torvalds1da177e2005-04-16 15:20:36 -070065 * Nicolas Mailhot (Nicolas.Mailhot@email.enst.fr)
Jeff Garzikd61780c2005-10-30 15:01:51 -080066 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070067 */
68
69#define VERSION "1.11"
70
71
72/*
73 * There is a bunch of documentation about the card, jumpers, config
74 * settings, restrictions, cables, device names and numbers in
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 Garzikd61780c2005-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 Garzikd61780c2005-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 Garzikd61780c2005-10-30 15:01:51 -0800195 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196 * Service functions for specialix IO8+ driver.
Jeff Garzikd61780c2005-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 Garzikd61780c2005-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 Garzikd61780c2005-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 Garzikd61780c2005-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 Garzikd61780c2005-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 Garzikd61780c2005-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 Garzikd61780c2005-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 Garzikd61780c2005-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 Garzikd61780c2005-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 Garzikd61780c2005-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 Garzikd61780c2005-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 Garzikd61780c2005-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 Garzikd61780c2005-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 Garzikd61780c2005-10-30 15:01:51 -0800473 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 func_exit();
Jeff Garzikd61780c2005-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 Garzikd61780c2005-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 Garzikd61780c2005-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 Garzikd61780c2005-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 Garzikd61780c2005-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 Garzikd61780c2005-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 Garzikd61780c2005-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 Garzikd61780c2005-10-30 15:01:51 -0800610
611 } else if (status & RCSR_PE)
Alan Cox33f0f882006-01-09 20:54:13 -0800612 flag = TTY_PARITY;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800613
614 else if (status & RCSR_FE)
Alan Cox33f0f882006-01-09 20:54:13 -0800615 flag = TTY_FRAME;
Jeff Garzikd61780c2005-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 Garzikd61780c2005-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 Garzikd61780c2005-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 Garzikd61780c2005-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 Garzikd61780c2005-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 Garzikd61780c2005-10-30 15:01:51 -0800648
Alan Cox33f0f882006-01-09 20:54:13 -0800649 tty_buffer_request_room(tty, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650
Alan Cox33f0f882006-01-09 20:54:13 -0800651 while (count--)
652 tty_insert_flip_char(tty, sx_in(bp, CD186x_RDR), TTY_NORMAL);
653 tty_flip_buffer_push(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 func_exit();
655}
656
657
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100658static void sx_transmit(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659{
660 struct specialix_port *port;
661 struct tty_struct *tty;
662 unsigned char count;
663
664 func_enter();
Alan Coxa72492b2008-07-22 11:17:05 +0100665 port = sx_get_port(bp, "Transmit");
666 if (port == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667 func_exit();
668 return;
669 }
Alan Coxa72492b2008-07-22 11:17:05 +0100670 dprintk(SX_DEBUG_TX, "port: %p\n", port);
Alan Cox44b7d1b2008-07-16 21:57:18 +0100671 tty = port->port.tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800672
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673 if (port->IER & IER_TXEMPTY) {
674 /* FIFO drained */
675 sx_out(bp, CD186x_CAR, port_No(port));
676 port->IER &= ~IER_TXEMPTY;
677 sx_out(bp, CD186x_IER, port->IER);
678 func_exit();
679 return;
680 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800681
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682 if ((port->xmit_cnt <= 0 && !port->break_length)
683 || tty->stopped || tty->hw_stopped) {
684 sx_out(bp, CD186x_CAR, port_No(port));
685 port->IER &= ~IER_TXRDY;
686 sx_out(bp, CD186x_IER, port->IER);
687 func_exit();
688 return;
689 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800690
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691 if (port->break_length) {
692 if (port->break_length > 0) {
693 if (port->COR2 & COR2_ETC) {
694 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
695 sx_out(bp, CD186x_TDR, CD186x_C_SBRK);
696 port->COR2 &= ~COR2_ETC;
697 }
698 count = min_t(int, port->break_length, 0xff);
699 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
700 sx_out(bp, CD186x_TDR, CD186x_C_DELAY);
701 sx_out(bp, CD186x_TDR, count);
Alan Coxa72492b2008-07-22 11:17:05 +0100702 port->break_length -= count;
703 if (port->break_length == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704 port->break_length--;
705 } else {
706 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
707 sx_out(bp, CD186x_TDR, CD186x_C_EBRK);
708 sx_out(bp, CD186x_COR2, port->COR2);
709 sx_wait_CCR(bp);
710 sx_out(bp, CD186x_CCR, CCR_CORCHG2);
711 port->break_length = 0;
712 }
713
714 func_exit();
715 return;
716 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800717
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718 count = CD186x_NFIFO;
719 do {
720 sx_out(bp, CD186x_TDR, port->xmit_buf[port->xmit_tail++]);
721 port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1);
722 if (--port->xmit_cnt <= 0)
723 break;
724 } while (--count > 0);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800725
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726 if (port->xmit_cnt <= 0) {
727 sx_out(bp, CD186x_CAR, port_No(port));
728 port->IER &= ~IER_TXRDY;
729 sx_out(bp, CD186x_IER, port->IER);
730 }
731 if (port->xmit_cnt <= port->wakeup_chars)
Alan Coxa72492b2008-07-22 11:17:05 +0100732 tty_wakeup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733
734 func_exit();
735}
736
737
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100738static void sx_check_modem(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739{
740 struct specialix_port *port;
741 struct tty_struct *tty;
742 unsigned char mcr;
743 int msvr_cd;
744
Alan Coxa72492b2008-07-22 11:17:05 +0100745 dprintk(SX_DEBUG_SIGNALS, "Modem intr. ");
746 port = sx_get_port(bp, "Modem");
747 if (port == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800749
Alan Cox44b7d1b2008-07-16 21:57:18 +0100750 tty = port->port.tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800751
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752 mcr = sx_in(bp, CD186x_MCR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753
754 if ((mcr & MCR_CDCHG)) {
Alan Coxa72492b2008-07-22 11:17:05 +0100755 dprintk(SX_DEBUG_SIGNALS, "CD just changed... ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756 msvr_cd = sx_in(bp, CD186x_MSVR) & MSVR_CD;
757 if (msvr_cd) {
Alan Coxa72492b2008-07-22 11:17:05 +0100758 dprintk(SX_DEBUG_SIGNALS, "Waking up guys in open.\n");
Alan Cox44b7d1b2008-07-16 21:57:18 +0100759 wake_up_interruptible(&port->port.open_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760 } else {
Alan Coxa72492b2008-07-22 11:17:05 +0100761 dprintk(SX_DEBUG_SIGNALS, "Sending HUP.\n");
Jiri Slabyd0d4e1c2008-02-07 00:16:39 -0800762 tty_hangup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763 }
764 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800765
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
767 if (mcr & MCR_CTSCHG) {
768 if (sx_in(bp, CD186x_MSVR) & MSVR_CTS) {
769 tty->hw_stopped = 0;
770 port->IER |= IER_TXRDY;
771 if (port->xmit_cnt <= port->wakeup_chars)
Jiri Slabyd0d4e1c2008-02-07 00:16:39 -0800772 tty_wakeup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773 } else {
774 tty->hw_stopped = 1;
775 port->IER &= ~IER_TXRDY;
776 }
777 sx_out(bp, CD186x_IER, port->IER);
778 }
779 if (mcr & MCR_DSSXHG) {
780 if (sx_in(bp, CD186x_MSVR) & MSVR_DSR) {
781 tty->hw_stopped = 0;
782 port->IER |= IER_TXRDY;
783 if (port->xmit_cnt <= port->wakeup_chars)
Jiri Slabyd0d4e1c2008-02-07 00:16:39 -0800784 tty_wakeup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 } else {
786 tty->hw_stopped = 1;
787 port->IER &= ~IER_TXRDY;
788 }
789 sx_out(bp, CD186x_IER, port->IER);
790 }
791#endif /* SPECIALIX_BRAIN_DAMAGED_CTS */
Jeff Garzikd61780c2005-10-30 15:01:51 -0800792
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 /* Clear change bits */
794 sx_out(bp, CD186x_MCR, 0);
795}
796
797
798/* The main interrupt processing routine */
Jeff Garzika6f97b22007-10-31 05:20:49 -0400799static irqreturn_t sx_interrupt(int dummy, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800{
801 unsigned char status;
802 unsigned char ack;
Jeff Garzika6f97b22007-10-31 05:20:49 -0400803 struct specialix_board *bp = dev_id;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804 unsigned long loop = 0;
805 int saved_reg;
806 unsigned long flags;
807
808 func_enter();
809
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810 spin_lock_irqsave(&bp->lock, flags);
811
Alan Coxa72492b2008-07-22 11:17:05 +0100812 dprintk(SX_DEBUG_FLOW, "enter %s port %d room: %ld\n", __func__,
813 port_No(sx_get_port(bp, "INT")),
814 SERIAL_XMIT_SIZE - sx_get_port(bp, "ITN")->xmit_cnt - 1);
Jeff Garzikc7bec5a2006-10-06 15:00:58 -0400815 if (!(bp->flags & SX_BOARD_ACTIVE)) {
Alan Coxa72492b2008-07-22 11:17:05 +0100816 dprintk(SX_DEBUG_IRQ, "sx: False interrupt. irq %d.\n",
817 bp->irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818 spin_unlock_irqrestore(&bp->lock, flags);
819 func_exit();
820 return IRQ_NONE;
821 }
822
823 saved_reg = bp->reg;
824
Alan Coxa72492b2008-07-22 11:17:05 +0100825 while (++loop < 16) {
826 status = sx_in(bp, CD186x_SRSR) &
827 (SRSR_RREQint | SRSR_TREQint | SRSR_MREQint);
828 if (status == 0)
829 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830 if (status & SRSR_RREQint) {
831 ack = sx_in(bp, CD186x_RRAR);
832
833 if (ack == (SX_ID | GIVR_IT_RCV))
834 sx_receive(bp);
835 else if (ack == (SX_ID | GIVR_IT_REXC))
836 sx_receive_exc(bp);
837 else
Alan Coxa72492b2008-07-22 11:17:05 +0100838 printk(KERN_ERR
839 "sx%d: status: 0x%x Bad receive ack 0x%02x.\n",
840 board_No(bp), status, ack);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800841
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842 } else if (status & SRSR_TREQint) {
843 ack = sx_in(bp, CD186x_TRAR);
844
845 if (ack == (SX_ID | GIVR_IT_TX))
846 sx_transmit(bp);
847 else
848 printk(KERN_ERR "sx%d: status: 0x%x Bad transmit ack 0x%02x. port: %d\n",
Alan Coxa72492b2008-07-22 11:17:05 +0100849 board_No(bp), status, ack,
850 port_No(sx_get_port(bp, "Int")));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851 } else if (status & SRSR_MREQint) {
852 ack = sx_in(bp, CD186x_MRAR);
853
Jeff Garzikd61780c2005-10-30 15:01:51 -0800854 if (ack == (SX_ID | GIVR_IT_MODEM))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855 sx_check_modem(bp);
856 else
Alan Coxa72492b2008-07-22 11:17:05 +0100857 printk(KERN_ERR
858 "sx%d: status: 0x%x Bad modem ack 0x%02x.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859 board_No(bp), status, ack);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800860
861 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862
863 sx_out(bp, CD186x_EOIR, 0); /* Mark end of interrupt */
864 }
865 bp->reg = saved_reg;
Alan Coxa72492b2008-07-22 11:17:05 +0100866 outb(bp->reg, bp->base + SX_ADDR_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867 spin_unlock_irqrestore(&bp->lock, flags);
868 func_exit();
869 return IRQ_HANDLED;
870}
871
872
873/*
874 * Routines for open & close processing.
875 */
876
Alan Coxa72492b2008-07-22 11:17:05 +0100877static void turn_ints_off(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878{
879 unsigned long flags;
880
881 func_enter();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700882 spin_lock_irqsave(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +0100883 (void) sx_in_off(bp, 0); /* Turn off interrupts. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884 spin_unlock_irqrestore(&bp->lock, flags);
885
886 func_exit();
887}
888
Alan Coxa72492b2008-07-22 11:17:05 +0100889static void turn_ints_on(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890{
891 unsigned long flags;
892
893 func_enter();
894
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895 spin_lock_irqsave(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +0100896 (void) sx_in(bp, 0); /* Turn ON interrupts. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897 spin_unlock_irqrestore(&bp->lock, flags);
898
899 func_exit();
900}
901
902
903/* Called with disabled interrupts */
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100904static int sx_setup_board(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905{
906 int error;
907
Jeff Garzikd61780c2005-10-30 15:01:51 -0800908 if (bp->flags & SX_BOARD_ACTIVE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700909 return 0;
910
911 if (bp->flags & SX_BOARD_IS_PCI)
Alan Coxa72492b2008-07-22 11:17:05 +0100912 error = request_irq(bp->irq, sx_interrupt,
913 IRQF_DISABLED | IRQF_SHARED, "specialix IO8+", bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914 else
Alan Coxa72492b2008-07-22 11:17:05 +0100915 error = request_irq(bp->irq, sx_interrupt,
916 IRQF_DISABLED, "specialix IO8+", bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917
Jeff Garzikd61780c2005-10-30 15:01:51 -0800918 if (error)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700919 return error;
920
Alan Coxa72492b2008-07-22 11:17:05 +0100921 turn_ints_on(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922 bp->flags |= SX_BOARD_ACTIVE;
923
924 return 0;
925}
926
927
928/* Called with disabled interrupts */
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100929static void sx_shutdown_board(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930{
931 func_enter();
932
933 if (!(bp->flags & SX_BOARD_ACTIVE)) {
934 func_exit();
935 return;
936 }
937
938 bp->flags &= ~SX_BOARD_ACTIVE;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800939
Alan Coxa72492b2008-07-22 11:17:05 +0100940 dprintk(SX_DEBUG_IRQ, "Freeing IRQ%d for board %d.\n",
941 bp->irq, board_No(bp));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942 free_irq(bp->irq, bp);
Alan Coxa72492b2008-07-22 11:17:05 +0100943 turn_ints_off(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944 func_exit();
945}
946
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100947static unsigned int sx_crtscts(struct tty_struct *tty)
948{
949 if (sx_rtscts)
950 return C_CRTSCTS(tty);
951 return 1;
952}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953
954/*
Jeff Garzikd61780c2005-10-30 15:01:51 -0800955 * Setting up port characteristics.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956 * Must be called with disabled interrupts
957 */
Alan Coxa72492b2008-07-22 11:17:05 +0100958static void sx_change_speed(struct specialix_board *bp,
959 struct specialix_port *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960{
961 struct tty_struct *tty;
962 unsigned long baud;
963 long tmp;
964 unsigned char cor1 = 0, cor3 = 0;
965 unsigned char mcor1 = 0, mcor2 = 0;
966 static unsigned long again;
967 unsigned long flags;
968
969 func_enter();
970
Alan Coxa72492b2008-07-22 11:17:05 +0100971 tty = port->port.tty;
972 if (!tty || !tty->termios) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973 func_exit();
974 return;
975 }
976
977 port->IER = 0;
978 port->COR2 = 0;
979 /* Select port on the board */
980 spin_lock_irqsave(&bp->lock, flags);
981 sx_out(bp, CD186x_CAR, port_No(port));
982
983 /* The Specialix board doens't implement the RTS lines.
984 They are used to set the IRQ level. Don't touch them. */
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100985 if (sx_crtscts(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986 port->MSVR = MSVR_DTR | (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
987 else
988 port->MSVR = (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
989 spin_unlock_irqrestore(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +0100990 dprintk(SX_DEBUG_TERMIOS, "sx: got MSVR=%02x.\n", port->MSVR);
Alan Cox67cc0162006-09-29 02:01:39 -0700991 baud = tty_get_baud_rate(tty);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800992
Alan Cox67cc0162006-09-29 02:01:39 -0700993 if (baud == 38400) {
Alan Cox44b7d1b2008-07-16 21:57:18 +0100994 if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
Adrian Bunka4bb2cf2006-10-17 00:10:00 -0700995 baud = 57600;
Alan Cox44b7d1b2008-07-16 21:57:18 +0100996 if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
Adrian Bunka4bb2cf2006-10-17 00:10:00 -0700997 baud = 115200;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700998 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800999
Alan Cox67cc0162006-09-29 02:01:39 -07001000 if (!baud) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001 /* Drop DTR & exit */
Alan Coxa72492b2008-07-22 11:17:05 +01001002 dprintk(SX_DEBUG_TERMIOS, "Dropping DTR... Hmm....\n");
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001003 if (!sx_crtscts(tty)) {
Alan Coxa72492b2008-07-22 11:17:05 +01001004 port->MSVR &= ~MSVR_DTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005 spin_lock_irqsave(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001006 sx_out(bp, CD186x_MSVR, port->MSVR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007 spin_unlock_irqrestore(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001008 } else
1009 dprintk(SX_DEBUG_TERMIOS, "Can't drop DTR: no DTR.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010 return;
1011 } else {
1012 /* Set DTR on */
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001013 if (!sx_crtscts(tty))
Alan Coxa72492b2008-07-22 11:17:05 +01001014 port->MSVR |= MSVR_DTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001016
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017 /*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001018 * Now we must calculate some speed depended things
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019 */
1020
1021 /* Set baud rate for port */
1022 tmp = port->custom_divisor ;
Alan Coxa72492b2008-07-22 11:17:05 +01001023 if (tmp)
1024 printk(KERN_INFO
1025 "sx%d: Using custom baud rate divisor %ld. \n"
1026 "This is an untested option, please be careful.\n",
1027 port_No(port), tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028 else
Alan Coxa72492b2008-07-22 11:17:05 +01001029 tmp = (((SX_OSCFREQ + baud/2) / baud + CD186x_TPC/2) /
1030 CD186x_TPC);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031
Alan Coxa72492b2008-07-22 11:17:05 +01001032 if (tmp < 0x10 && time_before(again, jiffies)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033 again = jiffies + HZ * 60;
1034 /* Page 48 of version 2.0 of the CL-CD1865 databook */
1035 if (tmp >= 12) {
Alan Coxa72492b2008-07-22 11:17:05 +01001036 printk(KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
1037 "Performance degradation is possible.\n"
1038 "Read specialix.txt for more info.\n",
1039 port_No(port), tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040 } else {
Alan Coxa72492b2008-07-22 11:17:05 +01001041 printk(KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
1042 "Warning: overstressing Cirrus chip. This might not work.\n"
1043 "Read specialix.txt for more info.\n", port_No(port), tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044 }
1045 }
1046 spin_lock_irqsave(&bp->lock, flags);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001047 sx_out(bp, CD186x_RBPRH, (tmp >> 8) & 0xff);
1048 sx_out(bp, CD186x_TBPRH, (tmp >> 8) & 0xff);
1049 sx_out(bp, CD186x_RBPRL, tmp & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050 sx_out(bp, CD186x_TBPRL, tmp & 0xff);
1051 spin_unlock_irqrestore(&bp->lock, flags);
Adrian Bunka4bb2cf2006-10-17 00:10:00 -07001052 if (port->custom_divisor)
Alan Coxa72492b2008-07-22 11:17:05 +01001053 baud = (SX_OSCFREQ + port->custom_divisor/2) /
1054 port->custom_divisor;
Adrian Bunka4bb2cf2006-10-17 00:10:00 -07001055 baud = (baud + 5) / 10; /* Estimated CPS */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056
1057 /* Two timer ticks seems enough to wakeup something like SLIP driver */
Jeff Garzikd61780c2005-10-30 15:01:51 -08001058 tmp = ((baud + HZ/2) / HZ) * 2 - CD186x_NFIFO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059 port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ?
1060 SERIAL_XMIT_SIZE - 1 : tmp);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001061
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062 /* Receiver timeout will be transmission time for 1.5 chars */
1063 tmp = (SPECIALIX_TPS + SPECIALIX_TPS/2 + baud/2) / baud;
1064 tmp = (tmp > 0xff) ? 0xff : tmp;
1065 spin_lock_irqsave(&bp->lock, flags);
1066 sx_out(bp, CD186x_RTPR, tmp);
1067 spin_unlock_irqrestore(&bp->lock, flags);
1068 switch (C_CSIZE(tty)) {
Alan Coxa72492b2008-07-22 11:17:05 +01001069 case CS5:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 cor1 |= COR1_5BITS;
1071 break;
Alan Coxa72492b2008-07-22 11:17:05 +01001072 case CS6:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073 cor1 |= COR1_6BITS;
1074 break;
Alan Coxa72492b2008-07-22 11:17:05 +01001075 case CS7:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076 cor1 |= COR1_7BITS;
1077 break;
Alan Coxa72492b2008-07-22 11:17:05 +01001078 case CS8:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079 cor1 |= COR1_8BITS;
1080 break;
1081 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001082
1083 if (C_CSTOPB(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084 cor1 |= COR1_2SB;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001085
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086 cor1 |= COR1_IGNORE;
1087 if (C_PARENB(tty)) {
1088 cor1 |= COR1_NORMPAR;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001089 if (C_PARODD(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090 cor1 |= COR1_ODDP;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001091 if (I_INPCK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092 cor1 &= ~COR1_IGNORE;
1093 }
1094 /* Set marking of some errors */
1095 port->mark_mask = RCSR_OE | RCSR_TOUT;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001096 if (I_INPCK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001097 port->mark_mask |= RCSR_FE | RCSR_PE;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001098 if (I_BRKINT(tty) || I_PARMRK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001099 port->mark_mask |= RCSR_BREAK;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001100 if (I_IGNPAR(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101 port->mark_mask &= ~(RCSR_FE | RCSR_PE);
1102 if (I_IGNBRK(tty)) {
1103 port->mark_mask &= ~RCSR_BREAK;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001104 if (I_IGNPAR(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105 /* Real raw mode. Ignore all */
1106 port->mark_mask &= ~RCSR_OE;
1107 }
1108 /* Enable Hardware Flow Control */
1109 if (C_CRTSCTS(tty)) {
1110#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
1111 port->IER |= IER_DSR | IER_CTS;
1112 mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD;
1113 mcor2 |= MCOR2_DSROD | MCOR2_CTSOD;
1114 spin_lock_irqsave(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001115 tty->hw_stopped = !(sx_in(bp, CD186x_MSVR) &
1116 (MSVR_CTS|MSVR_DSR));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117 spin_unlock_irqrestore(&bp->lock, flags);
1118#else
Jeff Garzikd61780c2005-10-30 15:01:51 -08001119 port->COR2 |= COR2_CTSAE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001120#endif
1121 }
1122 /* Enable Software Flow Control. FIXME: I'm not sure about this */
1123 /* Some people reported that it works, but I still doubt it */
1124 if (I_IXON(tty)) {
1125 port->COR2 |= COR2_TXIBE;
1126 cor3 |= (COR3_FCT | COR3_SCDE);
1127 if (I_IXANY(tty))
1128 port->COR2 |= COR2_IXM;
1129 spin_lock_irqsave(&bp->lock, flags);
1130 sx_out(bp, CD186x_SCHR1, START_CHAR(tty));
1131 sx_out(bp, CD186x_SCHR2, STOP_CHAR(tty));
1132 sx_out(bp, CD186x_SCHR3, START_CHAR(tty));
1133 sx_out(bp, CD186x_SCHR4, STOP_CHAR(tty));
1134 spin_unlock_irqrestore(&bp->lock, flags);
1135 }
1136 if (!C_CLOCAL(tty)) {
1137 /* Enable CD check */
1138 port->IER |= IER_CD;
1139 mcor1 |= MCOR1_CDZD;
1140 mcor2 |= MCOR2_CDOD;
1141 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001142
1143 if (C_CREAD(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144 /* Enable receiver */
1145 port->IER |= IER_RXD;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001146
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147 /* Set input FIFO size (1-8 bytes) */
1148 cor3 |= sx_rxfifo;
1149 /* Setting up CD186x channel registers */
1150 spin_lock_irqsave(&bp->lock, flags);
1151 sx_out(bp, CD186x_COR1, cor1);
1152 sx_out(bp, CD186x_COR2, port->COR2);
1153 sx_out(bp, CD186x_COR3, cor3);
1154 spin_unlock_irqrestore(&bp->lock, flags);
1155 /* Make CD186x know about registers change */
1156 sx_wait_CCR(bp);
1157 spin_lock_irqsave(&bp->lock, flags);
1158 sx_out(bp, CD186x_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3);
1159 /* Setting up modem option registers */
Alan Coxa72492b2008-07-22 11:17:05 +01001160 dprintk(SX_DEBUG_TERMIOS, "Mcor1 = %02x, mcor2 = %02x.\n",
1161 mcor1, mcor2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 sx_out(bp, CD186x_MCOR1, mcor1);
1163 sx_out(bp, CD186x_MCOR2, mcor2);
1164 spin_unlock_irqrestore(&bp->lock, flags);
1165 /* Enable CD186x transmitter & receiver */
1166 sx_wait_CCR(bp);
1167 spin_lock_irqsave(&bp->lock, flags);
1168 sx_out(bp, CD186x_CCR, CCR_TXEN | CCR_RXEN);
1169 /* Enable interrupts */
1170 sx_out(bp, CD186x_IER, port->IER);
1171 /* And finally set the modem lines... */
1172 sx_out(bp, CD186x_MSVR, port->MSVR);
1173 spin_unlock_irqrestore(&bp->lock, flags);
1174
1175 func_exit();
1176}
1177
1178
1179/* Must be called with interrupts enabled */
Alan Coxa72492b2008-07-22 11:17:05 +01001180static int sx_setup_port(struct specialix_board *bp,
1181 struct specialix_port *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182{
1183 unsigned long flags;
1184
1185 func_enter();
1186
Alan Cox44b7d1b2008-07-16 21:57:18 +01001187 if (port->port.flags & ASYNC_INITIALIZED) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001188 func_exit();
1189 return 0;
1190 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001191
Linus Torvalds1da177e2005-04-16 15:20:36 -07001192 if (!port->xmit_buf) {
1193 /* We may sleep in get_zeroed_page() */
1194 unsigned long tmp;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001195
Alan Coxa72492b2008-07-22 11:17:05 +01001196 tmp = get_zeroed_page(GFP_KERNEL);
1197 if (tmp == 0L) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198 func_exit();
1199 return -ENOMEM;
1200 }
1201
1202 if (port->xmit_buf) {
1203 free_page(tmp);
1204 func_exit();
1205 return -ERESTARTSYS;
1206 }
1207 port->xmit_buf = (unsigned char *) tmp;
1208 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001209
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210 spin_lock_irqsave(&port->lock, flags);
1211
Alan Cox44b7d1b2008-07-16 21:57:18 +01001212 if (port->port.tty)
1213 clear_bit(TTY_IO_ERROR, &port->port.tty->flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214
1215 port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
1216 sx_change_speed(bp, port);
Alan Cox44b7d1b2008-07-16 21:57:18 +01001217 port->port.flags |= ASYNC_INITIALIZED;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218
1219 spin_unlock_irqrestore(&port->lock, flags);
1220
Jeff Garzikd61780c2005-10-30 15:01:51 -08001221
Linus Torvalds1da177e2005-04-16 15:20:36 -07001222 func_exit();
1223 return 0;
1224}
1225
1226
1227/* Must be called with interrupts disabled */
Alan Coxa72492b2008-07-22 11:17:05 +01001228static void sx_shutdown_port(struct specialix_board *bp,
1229 struct specialix_port *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001230{
1231 struct tty_struct *tty;
1232 int i;
1233 unsigned long flags;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001234
Linus Torvalds1da177e2005-04-16 15:20:36 -07001235 func_enter();
1236
Alan Cox44b7d1b2008-07-16 21:57:18 +01001237 if (!(port->port.flags & ASYNC_INITIALIZED)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001238 func_exit();
1239 return;
1240 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001241
Linus Torvalds1da177e2005-04-16 15:20:36 -07001242 if (sx_debug & SX_DEBUG_FIFO) {
Alan Coxa72492b2008-07-22 11:17:05 +01001243 dprintk(SX_DEBUG_FIFO,
1244 "sx%d: port %d: %ld overruns, FIFO hits [ ",
1245 board_No(bp), port_No(port), port->overrun);
1246 for (i = 0; i < 10; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247 dprintk(SX_DEBUG_FIFO, "%ld ", port->hits[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001248 dprintk(SX_DEBUG_FIFO, "].\n");
1249 }
1250
1251 if (port->xmit_buf) {
1252 free_page((unsigned long) port->xmit_buf);
1253 port->xmit_buf = NULL;
1254 }
1255
1256 /* Select port */
1257 spin_lock_irqsave(&bp->lock, flags);
1258 sx_out(bp, CD186x_CAR, port_No(port));
1259
Alan Coxa72492b2008-07-22 11:17:05 +01001260 tty = port->port.tty;
1261 if (tty == NULL || C_HUPCL(tty)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001262 /* Drop DTR */
1263 sx_out(bp, CD186x_MSVDTR, 0);
1264 }
1265 spin_unlock_irqrestore(&bp->lock, flags);
1266 /* Reset port */
1267 sx_wait_CCR(bp);
1268 spin_lock_irqsave(&bp->lock, flags);
1269 sx_out(bp, CD186x_CCR, CCR_SOFTRESET);
1270 /* Disable all interrupts from this port */
1271 port->IER = 0;
1272 sx_out(bp, CD186x_IER, port->IER);
1273 spin_unlock_irqrestore(&bp->lock, flags);
1274 if (tty)
1275 set_bit(TTY_IO_ERROR, &tty->flags);
Alan Cox44b7d1b2008-07-16 21:57:18 +01001276 port->port.flags &= ~ASYNC_INITIALIZED;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001277
1278 if (!bp->count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279 sx_shutdown_board(bp);
1280 func_exit();
1281}
1282
Jeff Garzikd61780c2005-10-30 15:01:51 -08001283
Alan Coxa72492b2008-07-22 11:17:05 +01001284static int block_til_ready(struct tty_struct *tty, struct file *filp,
1285 struct specialix_port *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286{
1287 DECLARE_WAITQUEUE(wait, current);
1288 struct specialix_board *bp = port_Board(port);
1289 int retval;
1290 int do_clocal = 0;
1291 int CD;
1292 unsigned long flags;
1293
1294 func_enter();
1295
1296 /*
1297 * If the device is in the middle of being closed, then block
1298 * until it's done, and then try again.
1299 */
Alan Cox44b7d1b2008-07-16 21:57:18 +01001300 if (tty_hung_up_p(filp) || port->port.flags & ASYNC_CLOSING) {
1301 interruptible_sleep_on(&port->port.close_wait);
1302 if (port->port.flags & ASYNC_HUP_NOTIFY) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303 func_exit();
1304 return -EAGAIN;
1305 } else {
1306 func_exit();
1307 return -ERESTARTSYS;
1308 }
1309 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001310
Linus Torvalds1da177e2005-04-16 15:20:36 -07001311 /*
1312 * If non-blocking mode is set, or the port is not enabled,
1313 * then make the check up front and then exit.
1314 */
1315 if ((filp->f_flags & O_NONBLOCK) ||
1316 (tty->flags & (1 << TTY_IO_ERROR))) {
Alan Cox44b7d1b2008-07-16 21:57:18 +01001317 port->port.flags |= ASYNC_NORMAL_ACTIVE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001318 func_exit();
1319 return 0;
1320 }
1321
1322 if (C_CLOCAL(tty))
1323 do_clocal = 1;
1324
1325 /*
1326 * Block waiting for the carrier detect and the line to become
1327 * free (i.e., not in use by the callout). While we are in
1328 * this loop, info->count is dropped by one, so that
1329 * rs_close() knows when to free things. We restore it upon
1330 * exit, either normal or abnormal.
1331 */
1332 retval = 0;
Alan Cox44b7d1b2008-07-16 21:57:18 +01001333 add_wait_queue(&port->port.open_wait, &wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001334 spin_lock_irqsave(&port->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001335 if (!tty_hung_up_p(filp))
Alan Cox44b7d1b2008-07-16 21:57:18 +01001336 port->port.count--;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337 spin_unlock_irqrestore(&port->lock, flags);
Alan Cox44b7d1b2008-07-16 21:57:18 +01001338 port->port.blocked_open++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339 while (1) {
1340 spin_lock_irqsave(&bp->lock, flags);
1341 sx_out(bp, CD186x_CAR, port_No(port));
1342 CD = sx_in(bp, CD186x_MSVR) & MSVR_CD;
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001343 if (sx_crtscts(tty)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344 /* Activate RTS */
1345 port->MSVR |= MSVR_DTR; /* WTF? */
Alan Coxa72492b2008-07-22 11:17:05 +01001346 sx_out(bp, CD186x_MSVR, port->MSVR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001347 } else {
1348 /* Activate DTR */
1349 port->MSVR |= MSVR_DTR;
Alan Coxa72492b2008-07-22 11:17:05 +01001350 sx_out(bp, CD186x_MSVR, port->MSVR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001351 }
1352 spin_unlock_irqrestore(&bp->lock, flags);
1353 set_current_state(TASK_INTERRUPTIBLE);
1354 if (tty_hung_up_p(filp) ||
Alan Cox44b7d1b2008-07-16 21:57:18 +01001355 !(port->port.flags & ASYNC_INITIALIZED)) {
1356 if (port->port.flags & ASYNC_HUP_NOTIFY)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357 retval = -EAGAIN;
1358 else
Jeff Garzikd61780c2005-10-30 15:01:51 -08001359 retval = -ERESTARTSYS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001360 break;
1361 }
Alan Cox44b7d1b2008-07-16 21:57:18 +01001362 if (!(port->port.flags & ASYNC_CLOSING) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363 (do_clocal || CD))
1364 break;
1365 if (signal_pending(current)) {
1366 retval = -ERESTARTSYS;
1367 break;
1368 }
1369 schedule();
1370 }
1371
1372 set_current_state(TASK_RUNNING);
Alan Cox44b7d1b2008-07-16 21:57:18 +01001373 remove_wait_queue(&port->port.open_wait, &wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374 spin_lock_irqsave(&port->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001375 if (!tty_hung_up_p(filp))
Alan Cox44b7d1b2008-07-16 21:57:18 +01001376 port->port.count++;
Alan Cox44b7d1b2008-07-16 21:57:18 +01001377 port->port.blocked_open--;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001378 spin_unlock_irqrestore(&port->lock, flags);
1379 if (retval) {
1380 func_exit();
1381 return retval;
1382 }
1383
Alan Cox44b7d1b2008-07-16 21:57:18 +01001384 port->port.flags |= ASYNC_NORMAL_ACTIVE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001385 func_exit();
1386 return 0;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001387}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388
1389
Alan Coxa72492b2008-07-22 11:17:05 +01001390static int sx_open(struct tty_struct *tty, struct file *filp)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001391{
1392 int board;
1393 int error;
Alan Coxa72492b2008-07-22 11:17:05 +01001394 struct specialix_port *port;
1395 struct specialix_board *bp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001396 int i;
1397 unsigned long flags;
1398
1399 func_enter();
1400
1401 board = SX_BOARD(tty->index);
1402
1403 if (board >= SX_NBOARD || !(sx_board[board].flags & SX_BOARD_PRESENT)) {
1404 func_exit();
1405 return -ENODEV;
1406 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001407
Linus Torvalds1da177e2005-04-16 15:20:36 -07001408 bp = &sx_board[board];
1409 port = sx_port + board * SX_NPORT + SX_PORT(tty->index);
1410 port->overrun = 0;
1411 for (i = 0; i < 10; i++)
Alan Coxa72492b2008-07-22 11:17:05 +01001412 port->hits[i] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001413
Alan Coxa72492b2008-07-22 11:17:05 +01001414 dprintk(SX_DEBUG_OPEN,
1415 "Board = %d, bp = %p, port = %p, portno = %d.\n",
1416 board, bp, port, SX_PORT(tty->index));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001417
1418 if (sx_paranoia_check(port, tty->name, "sx_open")) {
1419 func_enter();
1420 return -ENODEV;
1421 }
1422
Alan Coxa72492b2008-07-22 11:17:05 +01001423 error = sx_setup_board(bp);
1424 if (error) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001425 func_exit();
1426 return error;
1427 }
1428
1429 spin_lock_irqsave(&bp->lock, flags);
Alan Cox44b7d1b2008-07-16 21:57:18 +01001430 port->port.count++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001431 bp->count++;
1432 tty->driver_data = port;
Alan Cox44b7d1b2008-07-16 21:57:18 +01001433 port->port.tty = tty;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001434 spin_unlock_irqrestore(&bp->lock, flags);
1435
Alan Coxa72492b2008-07-22 11:17:05 +01001436 error = sx_setup_port(bp, port);
1437 if (error) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001438 func_enter();
1439 return error;
1440 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001441
Alan Coxa72492b2008-07-22 11:17:05 +01001442 error = block_til_ready(tty, filp, port);
1443 if (error) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001444 func_enter();
1445 return error;
1446 }
1447
1448 func_exit();
1449 return 0;
1450}
1451
Alan Cox978e5952008-04-30 00:53:59 -07001452static void sx_flush_buffer(struct tty_struct *tty)
1453{
Alan Coxc9f19e92009-01-02 13:47:26 +00001454 struct specialix_port *port = tty->driver_data;
Alan Cox978e5952008-04-30 00:53:59 -07001455 unsigned long flags;
Alan Coxa72492b2008-07-22 11:17:05 +01001456 struct specialix_board *bp;
Alan Cox978e5952008-04-30 00:53:59 -07001457
1458 func_enter();
1459
1460 if (sx_paranoia_check(port, tty->name, "sx_flush_buffer")) {
1461 func_exit();
1462 return;
1463 }
1464
1465 bp = port_Board(port);
1466 spin_lock_irqsave(&port->lock, flags);
1467 port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
1468 spin_unlock_irqrestore(&port->lock, flags);
1469 tty_wakeup(tty);
1470
1471 func_exit();
1472}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001473
Alan Coxa72492b2008-07-22 11:17:05 +01001474static void sx_close(struct tty_struct *tty, struct file *filp)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001475{
Alan Coxc9f19e92009-01-02 13:47:26 +00001476 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001477 struct specialix_board *bp;
1478 unsigned long flags;
1479 unsigned long timeout;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001480
Linus Torvalds1da177e2005-04-16 15:20:36 -07001481 func_enter();
1482 if (!port || sx_paranoia_check(port, tty->name, "close")) {
1483 func_exit();
1484 return;
1485 }
1486 spin_lock_irqsave(&port->lock, flags);
1487
1488 if (tty_hung_up_p(filp)) {
1489 spin_unlock_irqrestore(&port->lock, flags);
1490 func_exit();
1491 return;
1492 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001493
Linus Torvalds1da177e2005-04-16 15:20:36 -07001494 bp = port_Board(port);
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001495 if (tty->count == 1 && port->port.count != 1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001496 printk(KERN_ERR "sx%d: sx_close: bad port count;"
1497 " tty->count is 1, port count is %d\n",
Alan Cox44b7d1b2008-07-16 21:57:18 +01001498 board_No(bp), port->port.count);
1499 port->port.count = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500 }
1501
Alan Cox44b7d1b2008-07-16 21:57:18 +01001502 if (port->port.count > 1) {
1503 port->port.count--;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001504 bp->count--;
1505
1506 spin_unlock_irqrestore(&port->lock, flags);
1507
1508 func_exit();
1509 return;
1510 }
Alan Cox44b7d1b2008-07-16 21:57:18 +01001511 port->port.flags |= ASYNC_CLOSING;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001512 /*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001513 * Now we wait for the transmit buffer to clear; and we notify
Linus Torvalds1da177e2005-04-16 15:20:36 -07001514 * the line discipline to only process XON/XOFF characters.
1515 */
1516 tty->closing = 1;
1517 spin_unlock_irqrestore(&port->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001518 dprintk(SX_DEBUG_OPEN, "Closing\n");
1519 if (port->port.closing_wait != ASYNC_CLOSING_WAIT_NONE)
Alan Cox44b7d1b2008-07-16 21:57:18 +01001520 tty_wait_until_sent(tty, port->port.closing_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001521 /*
1522 * At this point we stop accepting input. To do this, we
1523 * disable the receive line status interrupts, and tell the
1524 * interrupt driver to stop checking the data ready bit in the
1525 * line status register.
1526 */
Alan Coxa72492b2008-07-22 11:17:05 +01001527 dprintk(SX_DEBUG_OPEN, "Closed\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001528 port->IER &= ~IER_RXD;
Alan Cox44b7d1b2008-07-16 21:57:18 +01001529 if (port->port.flags & ASYNC_INITIALIZED) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001530 port->IER &= ~IER_TXRDY;
1531 port->IER |= IER_TXEMPTY;
1532 spin_lock_irqsave(&bp->lock, flags);
1533 sx_out(bp, CD186x_CAR, port_No(port));
1534 sx_out(bp, CD186x_IER, port->IER);
1535 spin_unlock_irqrestore(&bp->lock, flags);
1536 /*
1537 * Before we drop DTR, make sure the UART transmitter
1538 * has completely drained; this is especially
1539 * important if there is a transmit FIFO!
1540 */
1541 timeout = jiffies+HZ;
Alan Coxa72492b2008-07-22 11:17:05 +01001542 while (port->IER & IER_TXEMPTY) {
1543 set_current_state(TASK_INTERRUPTIBLE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544 msleep_interruptible(jiffies_to_msecs(port->timeout));
1545 if (time_after(jiffies, timeout)) {
Alan Coxa72492b2008-07-22 11:17:05 +01001546 printk(KERN_INFO "Timeout waiting for close\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001547 break;
1548 }
1549 }
1550
1551 }
1552
1553 if (--bp->count < 0) {
Alan Coxa72492b2008-07-22 11:17:05 +01001554 printk(KERN_ERR
1555 "sx%d: sx_shutdown_port: bad board count: %d port: %d\n",
1556 board_No(bp), bp->count, tty->index);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001557 bp->count = 0;
1558 }
Alan Cox44b7d1b2008-07-16 21:57:18 +01001559 if (--port->port.count < 0) {
Alan Coxa72492b2008-07-22 11:17:05 +01001560 printk(KERN_ERR
1561 "sx%d: sx_close: bad port count for tty%d: %d\n",
1562 board_No(bp), port_No(port), port->port.count);
Alan Cox44b7d1b2008-07-16 21:57:18 +01001563 port->port.count = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001564 }
1565
1566 sx_shutdown_port(bp, port);
Alan Cox978e5952008-04-30 00:53:59 -07001567 sx_flush_buffer(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001568 tty_ldisc_flush(tty);
1569 spin_lock_irqsave(&port->lock, flags);
1570 tty->closing = 0;
Alan Cox44b7d1b2008-07-16 21:57:18 +01001571 port->port.tty = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001572 spin_unlock_irqrestore(&port->lock, flags);
Alan Cox44b7d1b2008-07-16 21:57:18 +01001573 if (port->port.blocked_open) {
Alan Coxa72492b2008-07-22 11:17:05 +01001574 if (port->port.close_delay)
1575 msleep_interruptible(
1576 jiffies_to_msecs(port->port.close_delay));
Alan Cox44b7d1b2008-07-16 21:57:18 +01001577 wake_up_interruptible(&port->port.open_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001578 }
Alan Cox44b7d1b2008-07-16 21:57:18 +01001579 port->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
1580 wake_up_interruptible(&port->port.close_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001581
1582 func_exit();
1583}
1584
1585
Alan Coxa72492b2008-07-22 11:17:05 +01001586static int sx_write(struct tty_struct *tty,
1587 const unsigned char *buf, int count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001588{
Alan Coxc9f19e92009-01-02 13:47:26 +00001589 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001590 struct specialix_board *bp;
1591 int c, total = 0;
1592 unsigned long flags;
1593
1594 func_enter();
1595 if (sx_paranoia_check(port, tty->name, "sx_write")) {
1596 func_exit();
1597 return 0;
1598 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001599
Linus Torvalds1da177e2005-04-16 15:20:36 -07001600 bp = port_Board(port);
1601
Jiri Slaby365e0222006-09-30 23:28:11 -07001602 if (!port->xmit_buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001603 func_exit();
1604 return 0;
1605 }
1606
1607 while (1) {
1608 spin_lock_irqsave(&port->lock, flags);
1609 c = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
1610 SERIAL_XMIT_SIZE - port->xmit_head));
1611 if (c <= 0) {
1612 spin_unlock_irqrestore(&port->lock, flags);
1613 break;
1614 }
1615 memcpy(port->xmit_buf + port->xmit_head, buf, c);
1616 port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
1617 port->xmit_cnt += c;
1618 spin_unlock_irqrestore(&port->lock, flags);
1619
1620 buf += c;
1621 count -= c;
1622 total += c;
1623 }
1624
1625 spin_lock_irqsave(&bp->lock, flags);
1626 if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
1627 !(port->IER & IER_TXRDY)) {
1628 port->IER |= IER_TXRDY;
1629 sx_out(bp, CD186x_CAR, port_No(port));
1630 sx_out(bp, CD186x_IER, port->IER);
1631 }
1632 spin_unlock_irqrestore(&bp->lock, flags);
1633 func_exit();
1634
1635 return total;
1636}
1637
1638
Alan Coxa72492b2008-07-22 11:17:05 +01001639static int sx_put_char(struct tty_struct *tty, unsigned char ch)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001640{
Alan Coxc9f19e92009-01-02 13:47:26 +00001641 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001642 unsigned long flags;
Alan Coxa72492b2008-07-22 11:17:05 +01001643 struct specialix_board *bp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001644
1645 func_enter();
1646
1647 if (sx_paranoia_check(port, tty->name, "sx_put_char")) {
1648 func_exit();
Alan Cox6ae04572008-04-30 00:54:07 -07001649 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001650 }
Alan Coxa72492b2008-07-22 11:17:05 +01001651 dprintk(SX_DEBUG_TX, "check tty: %p %p\n", tty, port->xmit_buf);
Eric Sesterhenn326f28e92006-06-25 05:48:48 -07001652 if (!port->xmit_buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001653 func_exit();
Alan Cox6ae04572008-04-30 00:54:07 -07001654 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001655 }
1656 bp = port_Board(port);
1657 spin_lock_irqsave(&port->lock, flags);
1658
Alan Coxa72492b2008-07-22 11:17:05 +01001659 dprintk(SX_DEBUG_TX, "xmit_cnt: %d xmit_buf: %p\n",
1660 port->xmit_cnt, port->xmit_buf);
1661 if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1 || !port->xmit_buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001662 spin_unlock_irqrestore(&port->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001663 dprintk(SX_DEBUG_TX, "Exit size\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001664 func_exit();
Alan Cox6ae04572008-04-30 00:54:07 -07001665 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001666 }
Alan Coxa72492b2008-07-22 11:17:05 +01001667 dprintk(SX_DEBUG_TX, "Handle xmit: %p %p\n", port, port->xmit_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001668 port->xmit_buf[port->xmit_head++] = ch;
1669 port->xmit_head &= SERIAL_XMIT_SIZE - 1;
1670 port->xmit_cnt++;
1671 spin_unlock_irqrestore(&port->lock, flags);
1672
1673 func_exit();
Alan Cox6ae04572008-04-30 00:54:07 -07001674 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001675}
1676
1677
Alan Coxa72492b2008-07-22 11:17:05 +01001678static void sx_flush_chars(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001679{
Alan Coxc9f19e92009-01-02 13:47:26 +00001680 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001681 unsigned long flags;
Alan Coxa72492b2008-07-22 11:17:05 +01001682 struct specialix_board *bp = port_Board(port);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001683
1684 func_enter();
1685
1686 if (sx_paranoia_check(port, tty->name, "sx_flush_chars")) {
1687 func_exit();
1688 return;
1689 }
1690 if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
1691 !port->xmit_buf) {
1692 func_exit();
1693 return;
1694 }
1695 spin_lock_irqsave(&bp->lock, flags);
1696 port->IER |= IER_TXRDY;
1697 sx_out(port_Board(port), CD186x_CAR, port_No(port));
1698 sx_out(port_Board(port), CD186x_IER, port->IER);
1699 spin_unlock_irqrestore(&bp->lock, flags);
1700
1701 func_exit();
1702}
1703
1704
Alan Coxa72492b2008-07-22 11:17:05 +01001705static int sx_write_room(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001706{
Alan Coxc9f19e92009-01-02 13:47:26 +00001707 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001708 int ret;
1709
1710 func_enter();
1711
1712 if (sx_paranoia_check(port, tty->name, "sx_write_room")) {
1713 func_exit();
1714 return 0;
1715 }
1716
1717 ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;
1718 if (ret < 0)
1719 ret = 0;
1720
1721 func_exit();
1722 return ret;
1723}
1724
1725
1726static int sx_chars_in_buffer(struct tty_struct *tty)
1727{
Alan Coxc9f19e92009-01-02 13:47:26 +00001728 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001729
1730 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08001731
Linus Torvalds1da177e2005-04-16 15:20:36 -07001732 if (sx_paranoia_check(port, tty->name, "sx_chars_in_buffer")) {
1733 func_exit();
1734 return 0;
1735 }
1736 func_exit();
1737 return port->xmit_cnt;
1738}
1739
Linus Torvalds1da177e2005-04-16 15:20:36 -07001740static int sx_tiocmget(struct tty_struct *tty, struct file *file)
1741{
Alan Coxc9f19e92009-01-02 13:47:26 +00001742 struct specialix_port *port = tty->driver_data;
Alan Coxa72492b2008-07-22 11:17:05 +01001743 struct specialix_board *bp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001744 unsigned char status;
1745 unsigned int result;
1746 unsigned long flags;
1747
1748 func_enter();
1749
Harvey Harrisonbf9d8922008-04-30 00:55:10 -07001750 if (sx_paranoia_check(port, tty->name, __func__)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001751 func_exit();
1752 return -ENODEV;
1753 }
1754
1755 bp = port_Board(port);
Alan Coxa72492b2008-07-22 11:17:05 +01001756 spin_lock_irqsave(&bp->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001757 sx_out(bp, CD186x_CAR, port_No(port));
1758 status = sx_in(bp, CD186x_MSVR);
1759 spin_unlock_irqrestore(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001760 dprintk(SX_DEBUG_INIT, "Got msvr[%d] = %02x, car = %d.\n",
1761 port_No(port), status, sx_in(bp, CD186x_CAR));
1762 dprintk(SX_DEBUG_INIT, "sx_port = %p, port = %p\n", sx_port, port);
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001763 if (sx_crtscts(port->port.tty)) {
1764 result = TIOCM_DTR | TIOCM_DSR
Alan Coxa72492b2008-07-22 11:17:05 +01001765 | ((status & MSVR_DTR) ? TIOCM_RTS : 0)
1766 | ((status & MSVR_CD) ? TIOCM_CAR : 0)
Alan Coxa72492b2008-07-22 11:17:05 +01001767 | ((status & MSVR_CTS) ? TIOCM_CTS : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001768 } else {
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001769 result = TIOCM_RTS | TIOCM_DSR
Alan Coxa72492b2008-07-22 11:17:05 +01001770 | ((status & MSVR_DTR) ? TIOCM_DTR : 0)
1771 | ((status & MSVR_CD) ? TIOCM_CAR : 0)
Alan Coxa72492b2008-07-22 11:17:05 +01001772 | ((status & MSVR_CTS) ? TIOCM_CTS : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001773 }
1774
1775 func_exit();
1776
1777 return result;
1778}
1779
1780
1781static int sx_tiocmset(struct tty_struct *tty, struct file *file,
1782 unsigned int set, unsigned int clear)
1783{
Alan Coxc9f19e92009-01-02 13:47:26 +00001784 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001785 unsigned long flags;
1786 struct specialix_board *bp;
1787
1788 func_enter();
1789
Harvey Harrisonbf9d8922008-04-30 00:55:10 -07001790 if (sx_paranoia_check(port, tty->name, __func__)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001791 func_exit();
1792 return -ENODEV;
1793 }
1794
1795 bp = port_Board(port);
1796
1797 spin_lock_irqsave(&port->lock, flags);
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001798 if (sx_crtscts(port->port.tty)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001799 if (set & TIOCM_RTS)
1800 port->MSVR |= MSVR_DTR;
1801 } else {
1802 if (set & TIOCM_DTR)
1803 port->MSVR |= MSVR_DTR;
1804 }
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001805 if (sx_crtscts(port->port.tty)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001806 if (clear & TIOCM_RTS)
1807 port->MSVR &= ~MSVR_DTR;
1808 } else {
1809 if (clear & TIOCM_DTR)
1810 port->MSVR &= ~MSVR_DTR;
1811 }
Julia Lawall25470252009-07-20 16:05:07 +01001812 spin_lock(&bp->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001813 sx_out(bp, CD186x_CAR, port_No(port));
1814 sx_out(bp, CD186x_MSVR, port->MSVR);
Julia Lawall25470252009-07-20 16:05:07 +01001815 spin_unlock(&bp->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001816 spin_unlock_irqrestore(&port->lock, flags);
1817 func_exit();
1818 return 0;
1819}
1820
1821
Alan Coxfaa76122008-07-22 11:19:05 +01001822static int sx_send_break(struct tty_struct *tty, int length)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001823{
Alan Coxc9f19e92009-01-02 13:47:26 +00001824 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001825 struct specialix_board *bp = port_Board(port);
1826 unsigned long flags;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001827
Linus Torvalds1da177e2005-04-16 15:20:36 -07001828 func_enter();
Alan Coxfaa76122008-07-22 11:19:05 +01001829 if (length == 0 || length == -1)
1830 return -EOPNOTSUPP;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001831
Alan Coxa72492b2008-07-22 11:17:05 +01001832 spin_lock_irqsave(&port->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001833 port->break_length = SPECIALIX_TPS / HZ * length;
1834 port->COR2 |= COR2_ETC;
1835 port->IER |= IER_TXRDY;
Julia Lawall25470252009-07-20 16:05:07 +01001836 spin_lock(&bp->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001837 sx_out(bp, CD186x_CAR, port_No(port));
1838 sx_out(bp, CD186x_COR2, port->COR2);
1839 sx_out(bp, CD186x_IER, port->IER);
Julia Lawall25470252009-07-20 16:05:07 +01001840 spin_unlock(&bp->lock);
Alan Coxa72492b2008-07-22 11:17:05 +01001841 spin_unlock_irqrestore(&port->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001842 sx_wait_CCR(bp);
1843 spin_lock_irqsave(&bp->lock, flags);
1844 sx_out(bp, CD186x_CCR, CCR_CORCHG2);
1845 spin_unlock_irqrestore(&bp->lock, flags);
1846 sx_wait_CCR(bp);
1847
1848 func_exit();
Alan Coxfaa76122008-07-22 11:19:05 +01001849 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001850}
1851
1852
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001853static int sx_set_serial_info(struct specialix_port *port,
Alan Coxa72492b2008-07-22 11:17:05 +01001854 struct serial_struct __user *newinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001855{
1856 struct serial_struct tmp;
1857 struct specialix_board *bp = port_Board(port);
1858 int change_speed;
1859
1860 func_enter();
Alan Coxb190e172008-04-30 00:53:22 -07001861
Linus Torvalds1da177e2005-04-16 15:20:36 -07001862 if (copy_from_user(&tmp, newinfo, sizeof(tmp))) {
1863 func_enter();
1864 return -EFAULT;
1865 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001866
Alan Coxb190e172008-04-30 00:53:22 -07001867 lock_kernel();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001868
Alan Cox44b7d1b2008-07-16 21:57:18 +01001869 change_speed = ((port->port.flags & ASYNC_SPD_MASK) !=
Linus Torvalds1da177e2005-04-16 15:20:36 -07001870 (tmp.flags & ASYNC_SPD_MASK));
1871 change_speed |= (tmp.custom_divisor != port->custom_divisor);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001872
Linus Torvalds1da177e2005-04-16 15:20:36 -07001873 if (!capable(CAP_SYS_ADMIN)) {
Alan Cox44b7d1b2008-07-16 21:57:18 +01001874 if ((tmp.close_delay != port->port.close_delay) ||
1875 (tmp.closing_wait != port->port.closing_wait) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001876 ((tmp.flags & ~ASYNC_USR_MASK) !=
Alan Cox44b7d1b2008-07-16 21:57:18 +01001877 (port->port.flags & ~ASYNC_USR_MASK))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001878 func_exit();
Alan Coxb190e172008-04-30 00:53:22 -07001879 unlock_kernel();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001880 return -EPERM;
1881 }
Alan Cox44b7d1b2008-07-16 21:57:18 +01001882 port->port.flags = ((port->port.flags & ~ASYNC_USR_MASK) |
Alan Coxa72492b2008-07-22 11:17:05 +01001883 (tmp.flags & ASYNC_USR_MASK));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001884 port->custom_divisor = tmp.custom_divisor;
1885 } else {
Alan Cox44b7d1b2008-07-16 21:57:18 +01001886 port->port.flags = ((port->port.flags & ~ASYNC_FLAGS) |
Alan Coxa72492b2008-07-22 11:17:05 +01001887 (tmp.flags & ASYNC_FLAGS));
Alan Cox44b7d1b2008-07-16 21:57:18 +01001888 port->port.close_delay = tmp.close_delay;
1889 port->port.closing_wait = tmp.closing_wait;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001890 port->custom_divisor = tmp.custom_divisor;
1891 }
Alan Coxa72492b2008-07-22 11:17:05 +01001892 if (change_speed)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001893 sx_change_speed(bp, port);
Alan Coxa72492b2008-07-22 11:17:05 +01001894
Linus Torvalds1da177e2005-04-16 15:20:36 -07001895 func_exit();
Alan Coxb190e172008-04-30 00:53:22 -07001896 unlock_kernel();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001897 return 0;
1898}
1899
1900
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001901static int sx_get_serial_info(struct specialix_port *port,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001902 struct serial_struct __user *retinfo)
1903{
1904 struct serial_struct tmp;
1905 struct specialix_board *bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001906
Linus Torvalds1da177e2005-04-16 15:20:36 -07001907 func_enter();
1908
Linus Torvalds1da177e2005-04-16 15:20:36 -07001909 memset(&tmp, 0, sizeof(tmp));
Alan Coxb190e172008-04-30 00:53:22 -07001910 lock_kernel();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001911 tmp.type = PORT_CIRRUS;
1912 tmp.line = port - sx_port;
1913 tmp.port = bp->base;
1914 tmp.irq = bp->irq;
Alan Cox44b7d1b2008-07-16 21:57:18 +01001915 tmp.flags = port->port.flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001916 tmp.baud_base = (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC;
Alan Cox44b7d1b2008-07-16 21:57:18 +01001917 tmp.close_delay = port->port.close_delay * HZ/100;
1918 tmp.closing_wait = port->port.closing_wait * HZ/100;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001919 tmp.custom_divisor = port->custom_divisor;
1920 tmp.xmit_fifo_size = CD186x_NFIFO;
Alan Coxb190e172008-04-30 00:53:22 -07001921 unlock_kernel();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001922 if (copy_to_user(retinfo, &tmp, sizeof(tmp))) {
1923 func_exit();
1924 return -EFAULT;
1925 }
1926
1927 func_exit();
1928 return 0;
1929}
1930
1931
Alan Coxa72492b2008-07-22 11:17:05 +01001932static int sx_ioctl(struct tty_struct *tty, struct file *filp,
1933 unsigned int cmd, unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001934{
Alan Coxc9f19e92009-01-02 13:47:26 +00001935 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001936 void __user *argp = (void __user *)arg;
1937
1938 func_enter();
1939
1940 if (sx_paranoia_check(port, tty->name, "sx_ioctl")) {
1941 func_exit();
1942 return -ENODEV;
1943 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001944
Linus Torvalds1da177e2005-04-16 15:20:36 -07001945 switch (cmd) {
Alan Coxa72492b2008-07-22 11:17:05 +01001946 case TIOCGSERIAL:
Alan Coxfaa76122008-07-22 11:19:05 +01001947 func_exit();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001948 return sx_get_serial_info(port, argp);
Alan Coxa72492b2008-07-22 11:17:05 +01001949 case TIOCSSERIAL:
Alan Coxfaa76122008-07-22 11:19:05 +01001950 func_exit();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001951 return sx_set_serial_info(port, argp);
Alan Coxa72492b2008-07-22 11:17:05 +01001952 default:
Alan Coxfaa76122008-07-22 11:19:05 +01001953 func_exit();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001954 return -ENOIOCTLCMD;
1955 }
1956 func_exit();
1957 return 0;
1958}
1959
1960
Alan Coxa72492b2008-07-22 11:17:05 +01001961static void sx_throttle(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001962{
Alan Coxc9f19e92009-01-02 13:47:26 +00001963 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001964 struct specialix_board *bp;
1965 unsigned long flags;
1966
1967 func_enter();
1968
1969 if (sx_paranoia_check(port, tty->name, "sx_throttle")) {
1970 func_exit();
1971 return;
1972 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001973
Linus Torvalds1da177e2005-04-16 15:20:36 -07001974 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001975
Linus Torvalds1da177e2005-04-16 15:20:36 -07001976 /* Use DTR instead of RTS ! */
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001977 if (sx_crtscts(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001978 port->MSVR &= ~MSVR_DTR;
1979 else {
1980 /* Auch!!! I think the system shouldn't call this then. */
1981 /* Or maybe we're supposed (allowed?) to do our side of hw
Jeff Garzikd61780c2005-10-30 15:01:51 -08001982 handshake anyway, even when hardware handshake is off.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001983 When you see this in your logs, please report.... */
Alan Coxa72492b2008-07-22 11:17:05 +01001984 printk(KERN_ERR
1985 "sx%d: Need to throttle, but can't (hardware hs is off)\n",
1986 port_No(port));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001987 }
1988 spin_lock_irqsave(&bp->lock, flags);
1989 sx_out(bp, CD186x_CAR, port_No(port));
1990 spin_unlock_irqrestore(&bp->lock, flags);
1991 if (I_IXOFF(tty)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001992 sx_wait_CCR(bp);
1993 spin_lock_irqsave(&bp->lock, flags);
1994 sx_out(bp, CD186x_CCR, CCR_SSCH2);
1995 spin_unlock_irqrestore(&bp->lock, flags);
1996 sx_wait_CCR(bp);
1997 }
1998 spin_lock_irqsave(&bp->lock, flags);
1999 sx_out(bp, CD186x_MSVR, port->MSVR);
2000 spin_unlock_irqrestore(&bp->lock, flags);
2001
2002 func_exit();
2003}
2004
2005
Alan Coxa72492b2008-07-22 11:17:05 +01002006static void sx_unthrottle(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002007{
Alan Coxc9f19e92009-01-02 13:47:26 +00002008 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002009 struct specialix_board *bp;
2010 unsigned long flags;
2011
2012 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002013
Linus Torvalds1da177e2005-04-16 15:20:36 -07002014 if (sx_paranoia_check(port, tty->name, "sx_unthrottle")) {
2015 func_exit();
2016 return;
2017 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002018
Linus Torvalds1da177e2005-04-16 15:20:36 -07002019 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002020
Linus Torvalds1da177e2005-04-16 15:20:36 -07002021 spin_lock_irqsave(&port->lock, flags);
2022 /* XXXX Use DTR INSTEAD???? */
Alan Coxd2fbd0f2008-07-22 11:17:16 +01002023 if (sx_crtscts(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002024 port->MSVR |= MSVR_DTR;
Alan Coxa72492b2008-07-22 11:17:05 +01002025 /* Else clause: see remark in "sx_throttle"... */
Julia Lawall25470252009-07-20 16:05:07 +01002026 spin_lock(&bp->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002027 sx_out(bp, CD186x_CAR, port_No(port));
Julia Lawall25470252009-07-20 16:05:07 +01002028 spin_unlock(&bp->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002029 if (I_IXOFF(tty)) {
2030 spin_unlock_irqrestore(&port->lock, flags);
2031 sx_wait_CCR(bp);
2032 spin_lock_irqsave(&bp->lock, flags);
2033 sx_out(bp, CD186x_CCR, CCR_SSCH1);
2034 spin_unlock_irqrestore(&bp->lock, flags);
2035 sx_wait_CCR(bp);
2036 spin_lock_irqsave(&port->lock, flags);
2037 }
Julia Lawall25470252009-07-20 16:05:07 +01002038 spin_lock(&bp->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002039 sx_out(bp, CD186x_MSVR, port->MSVR);
Julia Lawall25470252009-07-20 16:05:07 +01002040 spin_unlock(&bp->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002041 spin_unlock_irqrestore(&port->lock, flags);
2042
2043 func_exit();
2044}
2045
2046
Alan Coxa72492b2008-07-22 11:17:05 +01002047static void sx_stop(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002048{
Alan Coxc9f19e92009-01-02 13:47:26 +00002049 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002050 struct specialix_board *bp;
2051 unsigned long flags;
2052
2053 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002054
Linus Torvalds1da177e2005-04-16 15:20:36 -07002055 if (sx_paranoia_check(port, tty->name, "sx_stop")) {
2056 func_exit();
2057 return;
2058 }
2059
2060 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002061
Linus Torvalds1da177e2005-04-16 15:20:36 -07002062 spin_lock_irqsave(&port->lock, flags);
2063 port->IER &= ~IER_TXRDY;
Julia Lawall25470252009-07-20 16:05:07 +01002064 spin_lock(&bp->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002065 sx_out(bp, CD186x_CAR, port_No(port));
2066 sx_out(bp, CD186x_IER, port->IER);
Julia Lawall25470252009-07-20 16:05:07 +01002067 spin_unlock(&bp->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002068 spin_unlock_irqrestore(&port->lock, flags);
2069
2070 func_exit();
2071}
2072
2073
Alan Coxa72492b2008-07-22 11:17:05 +01002074static void sx_start(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002075{
Alan Coxc9f19e92009-01-02 13:47:26 +00002076 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002077 struct specialix_board *bp;
2078 unsigned long flags;
2079
2080 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002081
Linus Torvalds1da177e2005-04-16 15:20:36 -07002082 if (sx_paranoia_check(port, tty->name, "sx_start")) {
2083 func_exit();
2084 return;
2085 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002086
Linus Torvalds1da177e2005-04-16 15:20:36 -07002087 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002088
Linus Torvalds1da177e2005-04-16 15:20:36 -07002089 spin_lock_irqsave(&port->lock, flags);
2090 if (port->xmit_cnt && port->xmit_buf && !(port->IER & IER_TXRDY)) {
2091 port->IER |= IER_TXRDY;
Julia Lawall25470252009-07-20 16:05:07 +01002092 spin_lock(&bp->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002093 sx_out(bp, CD186x_CAR, port_No(port));
2094 sx_out(bp, CD186x_IER, port->IER);
Julia Lawall25470252009-07-20 16:05:07 +01002095 spin_unlock(&bp->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002096 }
2097 spin_unlock_irqrestore(&port->lock, flags);
2098
2099 func_exit();
2100}
2101
Alan Coxa72492b2008-07-22 11:17:05 +01002102static void sx_hangup(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002103{
Alan Coxc9f19e92009-01-02 13:47:26 +00002104 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002105 struct specialix_board *bp;
2106 unsigned long flags;
2107
2108 func_enter();
2109
2110 if (sx_paranoia_check(port, tty->name, "sx_hangup")) {
2111 func_exit();
2112 return;
2113 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002114
Linus Torvalds1da177e2005-04-16 15:20:36 -07002115 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002116
Linus Torvalds1da177e2005-04-16 15:20:36 -07002117 sx_shutdown_port(bp, port);
2118 spin_lock_irqsave(&port->lock, flags);
Alan Cox44b7d1b2008-07-16 21:57:18 +01002119 bp->count -= port->port.count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002120 if (bp->count < 0) {
Alan Coxa72492b2008-07-22 11:17:05 +01002121 printk(KERN_ERR
2122 "sx%d: sx_hangup: bad board count: %d port: %d\n",
2123 board_No(bp), bp->count, tty->index);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002124 bp->count = 0;
2125 }
Alan Cox44b7d1b2008-07-16 21:57:18 +01002126 port->port.count = 0;
2127 port->port.flags &= ~ASYNC_NORMAL_ACTIVE;
2128 port->port.tty = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002129 spin_unlock_irqrestore(&port->lock, flags);
Alan Cox44b7d1b2008-07-16 21:57:18 +01002130 wake_up_interruptible(&port->port.open_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002131
2132 func_exit();
2133}
2134
2135
Alan Coxa72492b2008-07-22 11:17:05 +01002136static void sx_set_termios(struct tty_struct *tty,
2137 struct ktermios *old_termios)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002138{
Alan Coxc9f19e92009-01-02 13:47:26 +00002139 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002140 unsigned long flags;
Alan Coxa72492b2008-07-22 11:17:05 +01002141 struct specialix_board *bp;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002142
Linus Torvalds1da177e2005-04-16 15:20:36 -07002143 if (sx_paranoia_check(port, tty->name, "sx_set_termios"))
2144 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002145
Linus Torvalds1da177e2005-04-16 15:20:36 -07002146 bp = port_Board(port);
2147 spin_lock_irqsave(&port->lock, flags);
2148 sx_change_speed(port_Board(port), port);
2149 spin_unlock_irqrestore(&port->lock, flags);
2150
2151 if ((old_termios->c_cflag & CRTSCTS) &&
2152 !(tty->termios->c_cflag & CRTSCTS)) {
2153 tty->hw_stopped = 0;
2154 sx_start(tty);
2155 }
2156}
2157
Jeff Dikeb68e31d2006-10-02 02:17:18 -07002158static const struct tty_operations sx_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002159 .open = sx_open,
2160 .close = sx_close,
2161 .write = sx_write,
2162 .put_char = sx_put_char,
2163 .flush_chars = sx_flush_chars,
2164 .write_room = sx_write_room,
2165 .chars_in_buffer = sx_chars_in_buffer,
2166 .flush_buffer = sx_flush_buffer,
2167 .ioctl = sx_ioctl,
2168 .throttle = sx_throttle,
2169 .unthrottle = sx_unthrottle,
2170 .set_termios = sx_set_termios,
2171 .stop = sx_stop,
2172 .start = sx_start,
2173 .hangup = sx_hangup,
2174 .tiocmget = sx_tiocmget,
2175 .tiocmset = sx_tiocmset,
Alan Coxfaa76122008-07-22 11:19:05 +01002176 .break_ctl = sx_send_break,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002177};
2178
2179static int sx_init_drivers(void)
2180{
2181 int error;
2182 int i;
2183
2184 func_enter();
2185
2186 specialix_driver = alloc_tty_driver(SX_NBOARD * SX_NPORT);
2187 if (!specialix_driver) {
2188 printk(KERN_ERR "sx: Couldn't allocate tty_driver.\n");
2189 func_exit();
2190 return 1;
2191 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002192
Linus Torvalds1da177e2005-04-16 15:20:36 -07002193 specialix_driver->owner = THIS_MODULE;
2194 specialix_driver->name = "ttyW";
2195 specialix_driver->major = SPECIALIX_NORMAL_MAJOR;
2196 specialix_driver->type = TTY_DRIVER_TYPE_SERIAL;
2197 specialix_driver->subtype = SERIAL_TYPE_NORMAL;
2198 specialix_driver->init_termios = tty_std_termios;
2199 specialix_driver->init_termios.c_cflag =
2200 B9600 | CS8 | CREAD | HUPCL | CLOCAL;
Alan Cox606d0992006-12-08 02:38:45 -08002201 specialix_driver->init_termios.c_ispeed = 9600;
2202 specialix_driver->init_termios.c_ospeed = 9600;
Alan Coxfaa76122008-07-22 11:19:05 +01002203 specialix_driver->flags = TTY_DRIVER_REAL_RAW |
2204 TTY_DRIVER_HARDWARE_BREAK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002205 tty_set_operations(specialix_driver, &sx_ops);
2206
Alan Coxa72492b2008-07-22 11:17:05 +01002207 error = tty_register_driver(specialix_driver);
2208 if (error) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002209 put_tty_driver(specialix_driver);
Alan Coxa72492b2008-07-22 11:17:05 +01002210 printk(KERN_ERR
2211 "sx: Couldn't register specialix IO8+ driver, error = %d\n",
2212 error);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002213 func_exit();
2214 return 1;
2215 }
2216 memset(sx_port, 0, sizeof(sx_port));
2217 for (i = 0; i < SX_NPORT * SX_NBOARD; i++) {
2218 sx_port[i].magic = SPECIALIX_MAGIC;
Alan Cox44b7d1b2008-07-16 21:57:18 +01002219 tty_port_init(&sx_port[i].port);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002220 spin_lock_init(&sx_port[i].lock);
2221 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002222
Linus Torvalds1da177e2005-04-16 15:20:36 -07002223 func_exit();
2224 return 0;
2225}
2226
2227static void sx_release_drivers(void)
2228{
2229 func_enter();
2230
Linus Torvalds1da177e2005-04-16 15:20:36 -07002231 tty_unregister_driver(specialix_driver);
2232 put_tty_driver(specialix_driver);
2233 func_exit();
2234}
2235
Jeff Garzikd61780c2005-10-30 15:01:51 -08002236/*
2237 * This routine must be called by kernel at boot time
Linus Torvalds1da177e2005-04-16 15:20:36 -07002238 */
2239static int __init specialix_init(void)
2240{
2241 int i;
2242 int found = 0;
2243
2244 func_enter();
2245
2246 printk(KERN_INFO "sx: Specialix IO8+ driver v" VERSION ", (c) R.E.Wolff 1997/1998.\n");
2247 printk(KERN_INFO "sx: derived from work (c) D.Gorodchanin 1994-1996.\n");
Alan Coxd2fbd0f2008-07-22 11:17:16 +01002248 if (sx_rtscts)
2249 printk(KERN_INFO
2250 "sx: DTR/RTS pin is RTS when CRTSCTS is on.\n");
2251 else
2252 printk(KERN_INFO "sx: DTR/RTS pin is always RTS.\n");
Jeff Garzikd61780c2005-10-30 15:01:51 -08002253
Linus Torvalds1da177e2005-04-16 15:20:36 -07002254 for (i = 0; i < SX_NBOARD; i++)
Ingo Molnar34af9462006-06-27 02:53:55 -07002255 spin_lock_init(&sx_board[i].lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002256
2257 if (sx_init_drivers()) {
2258 func_exit();
2259 return -EIO;
2260 }
2261
Jeff Garzikd61780c2005-10-30 15:01:51 -08002262 for (i = 0; i < SX_NBOARD; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002263 if (sx_board[i].base && !sx_probe(&sx_board[i]))
2264 found++;
2265
2266#ifdef CONFIG_PCI
2267 {
2268 struct pci_dev *pdev = NULL;
2269
Alan Coxa72492b2008-07-22 11:17:05 +01002270 i = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002271 while (i < SX_NBOARD) {
2272 if (sx_board[i].flags & SX_BOARD_PRESENT) {
2273 i++;
2274 continue;
2275 }
Alan Coxa72492b2008-07-22 11:17:05 +01002276 pdev = pci_get_device(PCI_VENDOR_ID_SPECIALIX,
2277 PCI_DEVICE_ID_SPECIALIX_IO8, pdev);
2278 if (!pdev)
2279 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002280
2281 if (pci_enable_device(pdev))
2282 continue;
2283
2284 sx_board[i].irq = pdev->irq;
2285
Alan Coxa72492b2008-07-22 11:17:05 +01002286 sx_board[i].base = pci_resource_start(pdev, 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002287
2288 sx_board[i].flags |= SX_BOARD_IS_PCI;
2289 if (!sx_probe(&sx_board[i]))
Alan Coxa72492b2008-07-22 11:17:05 +01002290 found++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002291 }
Alan Cox606d0992006-12-08 02:38:45 -08002292 /* May exit pci_get sequence early with lots of boards */
2293 if (pdev != NULL)
2294 pci_dev_put(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002295 }
2296#endif
2297
2298 if (!found) {
2299 sx_release_drivers();
2300 printk(KERN_INFO "sx: No specialix IO8+ boards detected.\n");
2301 func_exit();
2302 return -EIO;
2303 }
2304
2305 func_exit();
2306 return 0;
2307}
2308
2309static int iobase[SX_NBOARD] = {0,};
Alan Coxd2fbd0f2008-07-22 11:17:16 +01002310static int irq[SX_NBOARD] = {0,};
Linus Torvalds1da177e2005-04-16 15:20:36 -07002311
2312module_param_array(iobase, int, NULL, 0);
2313module_param_array(irq, int, NULL, 0);
2314module_param(sx_debug, int, 0);
Alan Coxd2fbd0f2008-07-22 11:17:16 +01002315module_param(sx_rtscts, int, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002316module_param(sx_rxfifo, int, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002317
2318/*
2319 * You can setup up to 4 boards.
2320 * by specifying "iobase=0xXXX,0xXXX ..." as insmod parameter.
Jeff Garzikd61780c2005-10-30 15:01:51 -08002321 * You should specify the IRQs too in that case "irq=....,...".
2322 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07002323 * More than 4 boards in one computer is not possible, as the card can
Jeff Garzikd61780c2005-10-30 15:01:51 -08002324 * only use 4 different interrupts.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002325 *
2326 */
2327static int __init specialix_init_module(void)
2328{
2329 int i;
2330
2331 func_enter();
2332
Linus Torvalds1da177e2005-04-16 15:20:36 -07002333 if (iobase[0] || iobase[1] || iobase[2] || iobase[3]) {
Alan Coxa72492b2008-07-22 11:17:05 +01002334 for (i = 0; i < SX_NBOARD; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002335 sx_board[i].base = iobase[i];
2336 sx_board[i].irq = irq[i];
Alan Coxa72492b2008-07-22 11:17:05 +01002337 sx_board[i].count = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002338 }
2339 }
2340
2341 func_exit();
2342
2343 return specialix_init();
2344}
Jeff Garzikd61780c2005-10-30 15:01:51 -08002345
Linus Torvalds1da177e2005-04-16 15:20:36 -07002346static void __exit specialix_exit_module(void)
2347{
2348 int i;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002349
Linus Torvalds1da177e2005-04-16 15:20:36 -07002350 func_enter();
2351
2352 sx_release_drivers();
2353 for (i = 0; i < SX_NBOARD; i++)
Jeff Garzikd61780c2005-10-30 15:01:51 -08002354 if (sx_board[i].flags & SX_BOARD_PRESENT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002355 sx_release_io_range(&sx_board[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002356 func_exit();
2357}
2358
Chuck Short7691030b2006-07-10 04:43:59 -07002359static struct pci_device_id specialx_pci_tbl[] __devinitdata = {
2360 { PCI_DEVICE(PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_SPECIALIX_IO8) },
2361 { }
2362};
2363MODULE_DEVICE_TABLE(pci, specialx_pci_tbl);
2364
Linus Torvalds1da177e2005-04-16 15:20:36 -07002365module_init(specialix_init_module);
2366module_exit(specialix_exit_module);
2367
2368MODULE_LICENSE("GPL");
Scott James Remnant5350d3b2009-04-06 17:33:11 +01002369MODULE_ALIAS_CHARDEV_MAJOR(SPECIALIX_NORMAL_MAJOR);