blob: 9f8495b4fc8f5d648529d9c57950718bf11e8fd0 [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>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090097#include <linux/gfp.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070098
99#include "specialix_io8.h"
100#include "cd1865.h"
101
102
103/*
104 This driver can spew a whole lot of debugging output at you. If you
105 need maximum performance, you should disable the DEBUG define. To
106 aid in debugging in the field, I'm leaving the compile-time debug
107 features enabled, and disable them "runtime". That allows me to
108 instruct people with problems to enable debugging without requiring
109 them to recompile...
110*/
111#define DEBUG
112
113static int sx_debug;
114static int sx_rxfifo = SPECIALIX_RXFIFO;
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100115static int sx_rtscts;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116
117#ifdef DEBUG
Alan Coxa72492b2008-07-22 11:17:05 +0100118#define dprintk(f, str...) if (sx_debug & f) printk(str)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119#else
120#define dprintk(f, str...) /* nothing */
121#endif
122
123#define SX_DEBUG_FLOW 0x0001
124#define SX_DEBUG_DATA 0x0002
125#define SX_DEBUG_PROBE 0x0004
126#define SX_DEBUG_CHAN 0x0008
127#define SX_DEBUG_INIT 0x0010
128#define SX_DEBUG_RX 0x0020
129#define SX_DEBUG_TX 0x0040
130#define SX_DEBUG_IRQ 0x0080
131#define SX_DEBUG_OPEN 0x0100
132#define SX_DEBUG_TERMIOS 0x0200
133#define SX_DEBUG_SIGNALS 0x0400
134#define SX_DEBUG_FIFO 0x0800
135
136
Alan Coxa72492b2008-07-22 11:17:05 +0100137#define func_enter() dprintk(SX_DEBUG_FLOW, "io8: enter %s\n", __func__)
138#define func_exit() dprintk(SX_DEBUG_FLOW, "io8: exit %s\n", __func__)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140
141/* Configurable options: */
142
143/* Am I paranoid or not ? ;-) */
144#define SPECIALIX_PARANOIA_CHECK
145
Jeff Garzikd61780c2005-10-30 15:01:51 -0800146/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147 * The following defines are mostly for testing purposes. But if you need
148 * some nice reporting in your syslog, you can define them also.
149 */
150#undef SX_REPORT_FIFO
151#undef SX_REPORT_OVERRUN
152
153
154
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155
156#define SPECIALIX_LEGAL_FLAGS \
157 (ASYNC_HUP_NOTIFY | ASYNC_SAK | ASYNC_SPLIT_TERMIOS | \
158 ASYNC_SPD_HI | ASYNC_SPEED_VHI | ASYNC_SESSION_LOCKOUT | \
159 ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP)
160
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161static struct tty_driver *specialix_driver;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163static struct specialix_board sx_board[SX_NBOARD] = {
164 { 0, SX_IOBASE1, 9, },
165 { 0, SX_IOBASE2, 11, },
166 { 0, SX_IOBASE3, 12, },
167 { 0, SX_IOBASE4, 15, },
168};
169
170static struct specialix_port sx_port[SX_NBOARD * SX_NPORT];
171
172
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100173static int sx_paranoia_check(struct specialix_port const *port,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 char *name, const char *routine)
175{
176#ifdef SPECIALIX_PARANOIA_CHECK
Alan Coxa72492b2008-07-22 11:17:05 +0100177 static const char *badmagic = KERN_ERR
178 "sx: Warning: bad specialix port magic number for device %s in %s\n";
179 static const char *badinfo = KERN_ERR
180 "sx: Warning: null specialix port for device %s in %s\n";
Jeff Garzikd61780c2005-10-30 15:01:51 -0800181
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 if (!port) {
183 printk(badinfo, name, routine);
184 return 1;
185 }
186 if (port->magic != SPECIALIX_MAGIC) {
187 printk(badmagic, name, routine);
188 return 1;
189 }
190#endif
191 return 0;
192}
193
194
195/*
Jeff Garzikd61780c2005-10-30 15:01:51 -0800196 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197 * Service functions for specialix IO8+ driver.
Jeff Garzikd61780c2005-10-30 15:01:51 -0800198 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199 */
200
201/* Get board number from pointer */
Alan Coxa72492b2008-07-22 11:17:05 +0100202static inline int board_No(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203{
204 return bp - sx_board;
205}
206
207
208/* Get port number from pointer */
Alan Coxa72492b2008-07-22 11:17:05 +0100209static inline int port_No(struct specialix_port const *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210{
Jeff Garzikd61780c2005-10-30 15:01:51 -0800211 return SX_PORT(port - sx_port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212}
213
214
215/* Get pointer to board from pointer to port */
Alan Coxa72492b2008-07-22 11:17:05 +0100216static inline struct specialix_board *port_Board(
217 struct specialix_port const *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218{
219 return &sx_board[SX_BOARD(port - sx_port)];
220}
221
222
223/* Input Byte from CL CD186x register */
Alan Coxa72492b2008-07-22 11:17:05 +0100224static inline unsigned char sx_in(struct specialix_board *bp,
225 unsigned short reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226{
227 bp->reg = reg | 0x80;
Alan Coxa72492b2008-07-22 11:17:05 +0100228 outb(reg | 0x80, bp->base + SX_ADDR_REG);
229 return inb(bp->base + SX_DATA_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230}
231
232
233/* Output Byte to CL CD186x register */
Alan Coxa72492b2008-07-22 11:17:05 +0100234static inline void sx_out(struct specialix_board *bp, unsigned short reg,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235 unsigned char val)
236{
237 bp->reg = reg | 0x80;
Alan Coxa72492b2008-07-22 11:17:05 +0100238 outb(reg | 0x80, bp->base + SX_ADDR_REG);
239 outb(val, bp->base + SX_DATA_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240}
241
242
243/* Input Byte from CL CD186x register */
Alan Coxa72492b2008-07-22 11:17:05 +0100244static inline unsigned char sx_in_off(struct specialix_board *bp,
245 unsigned short reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246{
247 bp->reg = reg;
Alan Coxa72492b2008-07-22 11:17:05 +0100248 outb(reg, bp->base + SX_ADDR_REG);
249 return inb(bp->base + SX_DATA_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250}
251
252
253/* Output Byte to CL CD186x register */
Alan Coxa72492b2008-07-22 11:17:05 +0100254static inline void sx_out_off(struct specialix_board *bp,
255 unsigned short reg, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256{
257 bp->reg = reg;
Alan Coxa72492b2008-07-22 11:17:05 +0100258 outb(reg, bp->base + SX_ADDR_REG);
259 outb(val, bp->base + SX_DATA_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260}
261
262
263/* Wait for Channel Command Register ready */
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100264static void sx_wait_CCR(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265{
266 unsigned long delay, flags;
267 unsigned char ccr;
268
269 for (delay = SX_CCR_TIMEOUT; delay; delay--) {
270 spin_lock_irqsave(&bp->lock, flags);
271 ccr = sx_in(bp, CD186x_CCR);
272 spin_unlock_irqrestore(&bp->lock, flags);
273 if (!ccr)
274 return;
Alan Coxa72492b2008-07-22 11:17:05 +0100275 udelay(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800277
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp));
279}
280
281
282/* Wait for Channel Command Register ready */
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100283static void sx_wait_CCR_off(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284{
285 unsigned long delay;
286 unsigned char crr;
287 unsigned long flags;
288
289 for (delay = SX_CCR_TIMEOUT; delay; delay--) {
290 spin_lock_irqsave(&bp->lock, flags);
291 crr = sx_in_off(bp, CD186x_CCR);
292 spin_unlock_irqrestore(&bp->lock, flags);
293 if (!crr)
294 return;
Alan Coxa72492b2008-07-22 11:17:05 +0100295 udelay(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800297
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp));
299}
300
301
302/*
303 * specialix IO8+ IO range functions.
304 */
305
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100306static int sx_request_io_range(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307{
Jeff Garzikd61780c2005-10-30 15:01:51 -0800308 return request_region(bp->base,
309 bp->flags & SX_BOARD_IS_PCI ? SX_PCI_IO_SPACE : SX_IO_SPACE,
310 "specialix IO8+") == NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311}
312
313
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100314static void sx_release_io_range(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315{
Alan Coxa72492b2008-07-22 11:17:05 +0100316 release_region(bp->base, bp->flags & SX_BOARD_IS_PCI ?
317 SX_PCI_IO_SPACE : SX_IO_SPACE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318}
319
Jeff Garzikd61780c2005-10-30 15:01:51 -0800320
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321/* Set the IRQ using the RTS lines that run to the PAL on the board.... */
Alan Coxa72492b2008-07-22 11:17:05 +0100322static int sx_set_irq(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323{
324 int virq;
325 int i;
326 unsigned long flags;
327
Jeff Garzikd61780c2005-10-30 15:01:51 -0800328 if (bp->flags & SX_BOARD_IS_PCI)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329 return 1;
330 switch (bp->irq) {
331 /* In the same order as in the docs... */
Alan Coxa72492b2008-07-22 11:17:05 +0100332 case 15:
333 virq = 0;
334 break;
335 case 12:
336 virq = 1;
337 break;
338 case 11:
339 virq = 2;
340 break;
341 case 9:
342 virq = 3;
343 break;
344 default:printk(KERN_ERR
345 "Speclialix: cannot set irq to %d.\n", bp->irq);
346 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 }
348 spin_lock_irqsave(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +0100349 for (i = 0; i < 2; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350 sx_out(bp, CD186x_CAR, i);
351 sx_out(bp, CD186x_MSVRTS, ((virq >> i) & 0x1)? MSVR_RTS:0);
352 }
353 spin_unlock_irqrestore(&bp->lock, flags);
354 return 1;
355}
356
357
358/* Reset and setup CD186x chip */
Alan Coxa72492b2008-07-22 11:17:05 +0100359static int sx_init_CD186x(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360{
361 unsigned long flags;
362 int scaler;
363 int rv = 1;
364
365 func_enter();
366 sx_wait_CCR_off(bp); /* Wait for CCR ready */
367 spin_lock_irqsave(&bp->lock, flags);
368 sx_out_off(bp, CD186x_CCR, CCR_HARDRESET); /* Reset CD186x chip */
369 spin_unlock_irqrestore(&bp->lock, flags);
Jiri Slaby3e98cee2007-07-17 04:05:19 -0700370 msleep(50); /* Delay 0.05 sec */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 spin_lock_irqsave(&bp->lock, flags);
372 sx_out_off(bp, CD186x_GIVR, SX_ID); /* Set ID for this chip */
373 sx_out_off(bp, CD186x_GICR, 0); /* Clear all bits */
374 sx_out_off(bp, CD186x_PILR1, SX_ACK_MINT); /* Prio for modem intr */
375 sx_out_off(bp, CD186x_PILR2, SX_ACK_TINT); /* Prio for transmitter intr */
376 sx_out_off(bp, CD186x_PILR3, SX_ACK_RINT); /* Prio for receiver intr */
377 /* Set RegAckEn */
Alan Coxa72492b2008-07-22 11:17:05 +0100378 sx_out_off(bp, CD186x_SRCR, sx_in(bp, CD186x_SRCR) | SRCR_REGACKEN);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800379
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380 /* Setting up prescaler. We need 4 ticks per 1 ms */
381 scaler = SX_OSCFREQ/SPECIALIX_TPS;
382
383 sx_out_off(bp, CD186x_PPRH, scaler >> 8);
384 sx_out_off(bp, CD186x_PPRL, scaler & 0xff);
385 spin_unlock_irqrestore(&bp->lock, flags);
386
Alan Coxa72492b2008-07-22 11:17:05 +0100387 if (!sx_set_irq(bp)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 /* Figure out how to pass this along... */
Alan Coxa72492b2008-07-22 11:17:05 +0100389 printk(KERN_ERR "Cannot set irq to %d.\n", bp->irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390 rv = 0;
391 }
392
393 func_exit();
394 return rv;
395}
396
397
Alan Coxa72492b2008-07-22 11:17:05 +0100398static int read_cross_byte(struct specialix_board *bp, int reg, int bit)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399{
400 int i;
401 int t;
402 unsigned long flags;
403
404 spin_lock_irqsave(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +0100405 for (i = 0, t = 0; i < 8; i++) {
406 sx_out_off(bp, CD186x_CAR, i);
407 if (sx_in_off(bp, reg) & bit)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408 t |= 1 << i;
409 }
410 spin_unlock_irqrestore(&bp->lock, flags);
411
412 return t;
413}
414
415
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416/* Main probing routine, also sets irq. */
417static int sx_probe(struct specialix_board *bp)
418{
419 unsigned char val1, val2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420 int rev;
421 int chip;
422
423 func_enter();
424
Jeff Garzikd61780c2005-10-30 15:01:51 -0800425 if (sx_request_io_range(bp)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426 func_exit();
427 return 1;
428 }
429
430 /* Are the I/O ports here ? */
431 sx_out_off(bp, CD186x_PPRL, 0x5a);
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100432 udelay(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433 val1 = sx_in_off(bp, CD186x_PPRL);
434
435 sx_out_off(bp, CD186x_PPRL, 0xa5);
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100436 udelay(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437 val2 = sx_in_off(bp, CD186x_PPRL);
438
Jeff Garzikd61780c2005-10-30 15:01:51 -0800439
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100440 if (val1 != 0x5a || val2 != 0xa5) {
Alan Coxa72492b2008-07-22 11:17:05 +0100441 printk(KERN_INFO
442 "sx%d: specialix IO8+ Board at 0x%03x not found.\n",
443 board_No(bp), bp->base);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800444 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445 func_exit();
446 return 1;
447 }
448
Jeff Garzikd61780c2005-10-30 15:01:51 -0800449 /* Check the DSR lines that Specialix uses as board
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450 identification */
Alan Coxa72492b2008-07-22 11:17:05 +0100451 val1 = read_cross_byte(bp, CD186x_MSVR, MSVR_DSR);
452 val2 = read_cross_byte(bp, CD186x_MSVR, MSVR_RTS);
453 dprintk(SX_DEBUG_INIT,
454 "sx%d: DSR lines are: %02x, rts lines are: %02x\n",
455 board_No(bp), val1, val2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456
457 /* They managed to switch the bit order between the docs and
458 the IO8+ card. The new PCI card now conforms to old docs.
459 They changed the PCI docs to reflect the situation on the
460 old card. */
461 val2 = (bp->flags & SX_BOARD_IS_PCI)?0x4d : 0xb2;
462 if (val1 != val2) {
Alan Coxa72492b2008-07-22 11:17:05 +0100463 printk(KERN_INFO
464 "sx%d: specialix IO8+ ID %02x at 0x%03x not found (%02x).\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465 board_No(bp), val2, bp->base, val1);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800466 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467 func_exit();
468 return 1;
469 }
470
471
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 /* Reset CD186x again */
473 if (!sx_init_CD186x(bp)) {
Jeff Garzikd61780c2005-10-30 15:01:51 -0800474 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475 func_exit();
Jeff Garzikd61780c2005-10-30 15:01:51 -0800476 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477 }
478
479 sx_request_io_range(bp);
480 bp->flags |= SX_BOARD_PRESENT;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800481
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 /* Chip revcode pkgtype
Alan Coxa72492b2008-07-22 11:17:05 +0100483 GFRCR SRCR bit 7
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 CD180 rev B 0x81 0
485 CD180 rev C 0x82 0
486 CD1864 rev A 0x82 1
Jeff Garzikd61780c2005-10-30 15:01:51 -0800487 CD1865 rev A 0x83 1 -- Do not use!!! Does not work.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488 CD1865 rev B 0x84 1
489 -- Thanks to Gwen Wang, Cirrus Logic.
490 */
491
492 switch (sx_in_off(bp, CD186x_GFRCR)) {
Alan Coxa72492b2008-07-22 11:17:05 +0100493 case 0x82:
494 chip = 1864;
495 rev = 'A';
496 break;
497 case 0x83:
498 chip = 1865;
499 rev = 'A';
500 break;
501 case 0x84:
502 chip = 1865;
503 rev = 'B';
504 break;
505 case 0x85:
506 chip = 1865;
507 rev = 'C';
508 break; /* Does not exist at this time */
509 default:
510 chip = -1;
511 rev = 'x';
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512 }
513
Alan Coxa72492b2008-07-22 11:17:05 +0100514 dprintk(SX_DEBUG_INIT, " GFCR = 0x%02x\n", sx_in_off(bp, CD186x_GFRCR));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515
Alan Coxa72492b2008-07-22 11:17:05 +0100516 printk(KERN_INFO
517 "sx%d: specialix IO8+ board detected at 0x%03x, IRQ %d, CD%d Rev. %c.\n",
518 board_No(bp), bp->base, bp->irq, chip, rev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519
520 func_exit();
521 return 0;
522}
523
Jeff Garzikd61780c2005-10-30 15:01:51 -0800524/*
525 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526 * Interrupt processing routines.
527 * */
528
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100529static struct specialix_port *sx_get_port(struct specialix_board *bp,
Alan Coxa72492b2008-07-22 11:17:05 +0100530 unsigned char const *what)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531{
532 unsigned char channel;
Alan Coxa72492b2008-07-22 11:17:05 +0100533 struct specialix_port *port = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534
535 channel = sx_in(bp, CD186x_GICR) >> GICR_CHAN_OFF;
Alan Coxa72492b2008-07-22 11:17:05 +0100536 dprintk(SX_DEBUG_CHAN, "channel: %d\n", channel);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537 if (channel < CD186x_NCH) {
538 port = &sx_port[board_No(bp) * SX_NPORT + channel];
Alan Coxa72492b2008-07-22 11:17:05 +0100539 dprintk(SX_DEBUG_CHAN, "port: %d %p flags: 0x%lx\n",
540 board_No(bp) * SX_NPORT + channel, port,
541 port->port.flags & ASYNC_INITIALIZED);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542
Alan Cox44b7d1b2008-07-16 21:57:18 +0100543 if (port->port.flags & ASYNC_INITIALIZED) {
Alan Coxa72492b2008-07-22 11:17:05 +0100544 dprintk(SX_DEBUG_CHAN, "port: %d %p\n", channel, port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 func_exit();
546 return port;
547 }
548 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800549 printk(KERN_INFO "sx%d: %s interrupt from invalid port %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550 board_No(bp), what, channel);
551 return NULL;
552}
553
554
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100555static void sx_receive_exc(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556{
557 struct specialix_port *port;
558 struct tty_struct *tty;
559 unsigned char status;
Alan Cox33f0f882006-01-09 20:54:13 -0800560 unsigned char ch, flag;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561
562 func_enter();
563
564 port = sx_get_port(bp, "Receive");
565 if (!port) {
Alan Coxa72492b2008-07-22 11:17:05 +0100566 dprintk(SX_DEBUG_RX, "Hmm, couldn't find port.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567 func_exit();
568 return;
569 }
Alan Cox44b7d1b2008-07-16 21:57:18 +0100570 tty = port->port.tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800571
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572 status = sx_in(bp, CD186x_RCSR);
573
Alan Coxa72492b2008-07-22 11:17:05 +0100574 dprintk(SX_DEBUG_RX, "status: 0x%x\n", status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 if (status & RCSR_OE) {
576 port->overrun++;
Alan Coxa72492b2008-07-22 11:17:05 +0100577 dprintk(SX_DEBUG_FIFO,
578 "sx%d: port %d: Overrun. Total %ld overruns.\n",
579 board_No(bp), port_No(port), port->overrun);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580 }
581 status &= port->mark_mask;
582
583 /* This flip buffer check needs to be below the reading of the
584 status register to reset the chip's IRQ.... */
Alan Cox33f0f882006-01-09 20:54:13 -0800585 if (tty_buffer_request_room(tty, 1) == 0) {
Alan Coxa72492b2008-07-22 11:17:05 +0100586 dprintk(SX_DEBUG_FIFO,
587 "sx%d: port %d: Working around flip buffer overflow.\n",
588 board_No(bp), port_No(port));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589 func_exit();
590 return;
591 }
592
593 ch = sx_in(bp, CD186x_RDR);
594 if (!status) {
595 func_exit();
596 return;
597 }
598 if (status & RCSR_TOUT) {
Alan Coxa72492b2008-07-22 11:17:05 +0100599 printk(KERN_INFO
600 "sx%d: port %d: Receiver timeout. Hardware problems ?\n",
601 board_No(bp), port_No(port));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602 func_exit();
603 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800604
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605 } else if (status & RCSR_BREAK) {
606 dprintk(SX_DEBUG_RX, "sx%d: port %d: Handling break...\n",
607 board_No(bp), port_No(port));
Alan Cox33f0f882006-01-09 20:54:13 -0800608 flag = TTY_BREAK;
Alan Cox44b7d1b2008-07-16 21:57:18 +0100609 if (port->port.flags & ASYNC_SAK)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610 do_SAK(tty);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800611
612 } else if (status & RCSR_PE)
Alan Cox33f0f882006-01-09 20:54:13 -0800613 flag = TTY_PARITY;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800614
615 else if (status & RCSR_FE)
Alan Cox33f0f882006-01-09 20:54:13 -0800616 flag = TTY_FRAME;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800617
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 else if (status & RCSR_OE)
Alan Cox33f0f882006-01-09 20:54:13 -0800619 flag = TTY_OVERRUN;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800620
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621 else
Alan Cox33f0f882006-01-09 20:54:13 -0800622 flag = TTY_NORMAL;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800623
Alan Coxa72492b2008-07-22 11:17:05 +0100624 if (tty_insert_flip_char(tty, ch, flag))
Alan Cox33f0f882006-01-09 20:54:13 -0800625 tty_flip_buffer_push(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626 func_exit();
627}
628
629
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100630static void sx_receive(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631{
632 struct specialix_port *port;
633 struct tty_struct *tty;
634 unsigned char count;
635
636 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -0800637
Alan Coxa72492b2008-07-22 11:17:05 +0100638 port = sx_get_port(bp, "Receive");
639 if (port == NULL) {
640 dprintk(SX_DEBUG_RX, "Hmm, couldn't find port.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641 func_exit();
642 return;
643 }
Alan Cox44b7d1b2008-07-16 21:57:18 +0100644 tty = port->port.tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800645
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646 count = sx_in(bp, CD186x_RDCR);
Alan Coxa72492b2008-07-22 11:17:05 +0100647 dprintk(SX_DEBUG_RX, "port: %p: count: %d\n", port, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648 port->hits[count > 8 ? 9 : count]++;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800649
Alan Cox33f0f882006-01-09 20:54:13 -0800650 while (count--)
651 tty_insert_flip_char(tty, sx_in(bp, CD186x_RDR), TTY_NORMAL);
652 tty_flip_buffer_push(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653 func_exit();
654}
655
656
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100657static void sx_transmit(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658{
659 struct specialix_port *port;
660 struct tty_struct *tty;
661 unsigned char count;
662
663 func_enter();
Alan Coxa72492b2008-07-22 11:17:05 +0100664 port = sx_get_port(bp, "Transmit");
665 if (port == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666 func_exit();
667 return;
668 }
Alan Coxa72492b2008-07-22 11:17:05 +0100669 dprintk(SX_DEBUG_TX, "port: %p\n", port);
Alan Cox44b7d1b2008-07-16 21:57:18 +0100670 tty = port->port.tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800671
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672 if (port->IER & IER_TXEMPTY) {
673 /* FIFO drained */
674 sx_out(bp, CD186x_CAR, port_No(port));
675 port->IER &= ~IER_TXEMPTY;
676 sx_out(bp, CD186x_IER, port->IER);
677 func_exit();
678 return;
679 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800680
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681 if ((port->xmit_cnt <= 0 && !port->break_length)
682 || tty->stopped || tty->hw_stopped) {
683 sx_out(bp, CD186x_CAR, port_No(port));
684 port->IER &= ~IER_TXRDY;
685 sx_out(bp, CD186x_IER, port->IER);
686 func_exit();
687 return;
688 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800689
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 if (port->break_length) {
691 if (port->break_length > 0) {
692 if (port->COR2 & COR2_ETC) {
693 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
694 sx_out(bp, CD186x_TDR, CD186x_C_SBRK);
695 port->COR2 &= ~COR2_ETC;
696 }
697 count = min_t(int, port->break_length, 0xff);
698 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
699 sx_out(bp, CD186x_TDR, CD186x_C_DELAY);
700 sx_out(bp, CD186x_TDR, count);
Alan Coxa72492b2008-07-22 11:17:05 +0100701 port->break_length -= count;
702 if (port->break_length == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 port->break_length--;
704 } else {
705 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
706 sx_out(bp, CD186x_TDR, CD186x_C_EBRK);
707 sx_out(bp, CD186x_COR2, port->COR2);
708 sx_wait_CCR(bp);
709 sx_out(bp, CD186x_CCR, CCR_CORCHG2);
710 port->break_length = 0;
711 }
712
713 func_exit();
714 return;
715 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800716
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717 count = CD186x_NFIFO;
718 do {
719 sx_out(bp, CD186x_TDR, port->xmit_buf[port->xmit_tail++]);
720 port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1);
721 if (--port->xmit_cnt <= 0)
722 break;
723 } while (--count > 0);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800724
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725 if (port->xmit_cnt <= 0) {
726 sx_out(bp, CD186x_CAR, port_No(port));
727 port->IER &= ~IER_TXRDY;
728 sx_out(bp, CD186x_IER, port->IER);
729 }
730 if (port->xmit_cnt <= port->wakeup_chars)
Alan Coxa72492b2008-07-22 11:17:05 +0100731 tty_wakeup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732
733 func_exit();
734}
735
736
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100737static void sx_check_modem(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738{
739 struct specialix_port *port;
740 struct tty_struct *tty;
741 unsigned char mcr;
742 int msvr_cd;
743
Alan Coxa72492b2008-07-22 11:17:05 +0100744 dprintk(SX_DEBUG_SIGNALS, "Modem intr. ");
745 port = sx_get_port(bp, "Modem");
746 if (port == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800748
Alan Cox44b7d1b2008-07-16 21:57:18 +0100749 tty = port->port.tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800750
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751 mcr = sx_in(bp, CD186x_MCR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752
753 if ((mcr & MCR_CDCHG)) {
Alan Coxa72492b2008-07-22 11:17:05 +0100754 dprintk(SX_DEBUG_SIGNALS, "CD just changed... ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755 msvr_cd = sx_in(bp, CD186x_MSVR) & MSVR_CD;
756 if (msvr_cd) {
Alan Coxa72492b2008-07-22 11:17:05 +0100757 dprintk(SX_DEBUG_SIGNALS, "Waking up guys in open.\n");
Alan Cox44b7d1b2008-07-16 21:57:18 +0100758 wake_up_interruptible(&port->port.open_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759 } else {
Alan Coxa72492b2008-07-22 11:17:05 +0100760 dprintk(SX_DEBUG_SIGNALS, "Sending HUP.\n");
Jiri Slabyd0d4e1c2008-02-07 00:16:39 -0800761 tty_hangup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762 }
763 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800764
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
766 if (mcr & MCR_CTSCHG) {
767 if (sx_in(bp, CD186x_MSVR) & MSVR_CTS) {
768 tty->hw_stopped = 0;
769 port->IER |= IER_TXRDY;
770 if (port->xmit_cnt <= port->wakeup_chars)
Jiri Slabyd0d4e1c2008-02-07 00:16:39 -0800771 tty_wakeup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772 } else {
773 tty->hw_stopped = 1;
774 port->IER &= ~IER_TXRDY;
775 }
776 sx_out(bp, CD186x_IER, port->IER);
777 }
778 if (mcr & MCR_DSSXHG) {
779 if (sx_in(bp, CD186x_MSVR) & MSVR_DSR) {
780 tty->hw_stopped = 0;
781 port->IER |= IER_TXRDY;
782 if (port->xmit_cnt <= port->wakeup_chars)
Jiri Slabyd0d4e1c2008-02-07 00:16:39 -0800783 tty_wakeup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784 } else {
785 tty->hw_stopped = 1;
786 port->IER &= ~IER_TXRDY;
787 }
788 sx_out(bp, CD186x_IER, port->IER);
789 }
790#endif /* SPECIALIX_BRAIN_DAMAGED_CTS */
Jeff Garzikd61780c2005-10-30 15:01:51 -0800791
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792 /* Clear change bits */
793 sx_out(bp, CD186x_MCR, 0);
794}
795
796
797/* The main interrupt processing routine */
Jeff Garzika6f97b22007-10-31 05:20:49 -0400798static irqreturn_t sx_interrupt(int dummy, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799{
800 unsigned char status;
801 unsigned char ack;
Jeff Garzika6f97b22007-10-31 05:20:49 -0400802 struct specialix_board *bp = dev_id;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 unsigned long loop = 0;
804 int saved_reg;
805 unsigned long flags;
806
807 func_enter();
808
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809 spin_lock_irqsave(&bp->lock, flags);
810
Alan Coxa72492b2008-07-22 11:17:05 +0100811 dprintk(SX_DEBUG_FLOW, "enter %s port %d room: %ld\n", __func__,
812 port_No(sx_get_port(bp, "INT")),
813 SERIAL_XMIT_SIZE - sx_get_port(bp, "ITN")->xmit_cnt - 1);
Jeff Garzikc7bec5a2006-10-06 15:00:58 -0400814 if (!(bp->flags & SX_BOARD_ACTIVE)) {
Alan Coxa72492b2008-07-22 11:17:05 +0100815 dprintk(SX_DEBUG_IRQ, "sx: False interrupt. irq %d.\n",
816 bp->irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817 spin_unlock_irqrestore(&bp->lock, flags);
818 func_exit();
819 return IRQ_NONE;
820 }
821
822 saved_reg = bp->reg;
823
Alan Coxa72492b2008-07-22 11:17:05 +0100824 while (++loop < 16) {
825 status = sx_in(bp, CD186x_SRSR) &
826 (SRSR_RREQint | SRSR_TREQint | SRSR_MREQint);
827 if (status == 0)
828 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829 if (status & SRSR_RREQint) {
830 ack = sx_in(bp, CD186x_RRAR);
831
832 if (ack == (SX_ID | GIVR_IT_RCV))
833 sx_receive(bp);
834 else if (ack == (SX_ID | GIVR_IT_REXC))
835 sx_receive_exc(bp);
836 else
Alan Coxa72492b2008-07-22 11:17:05 +0100837 printk(KERN_ERR
838 "sx%d: status: 0x%x Bad receive ack 0x%02x.\n",
839 board_No(bp), status, ack);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800840
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841 } else if (status & SRSR_TREQint) {
842 ack = sx_in(bp, CD186x_TRAR);
843
844 if (ack == (SX_ID | GIVR_IT_TX))
845 sx_transmit(bp);
846 else
847 printk(KERN_ERR "sx%d: status: 0x%x Bad transmit ack 0x%02x. port: %d\n",
Alan Coxa72492b2008-07-22 11:17:05 +0100848 board_No(bp), status, ack,
849 port_No(sx_get_port(bp, "Int")));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850 } else if (status & SRSR_MREQint) {
851 ack = sx_in(bp, CD186x_MRAR);
852
Jeff Garzikd61780c2005-10-30 15:01:51 -0800853 if (ack == (SX_ID | GIVR_IT_MODEM))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854 sx_check_modem(bp);
855 else
Alan Coxa72492b2008-07-22 11:17:05 +0100856 printk(KERN_ERR
857 "sx%d: status: 0x%x Bad modem ack 0x%02x.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858 board_No(bp), status, ack);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800859
860 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861
862 sx_out(bp, CD186x_EOIR, 0); /* Mark end of interrupt */
863 }
864 bp->reg = saved_reg;
Alan Coxa72492b2008-07-22 11:17:05 +0100865 outb(bp->reg, bp->base + SX_ADDR_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866 spin_unlock_irqrestore(&bp->lock, flags);
867 func_exit();
868 return IRQ_HANDLED;
869}
870
871
872/*
873 * Routines for open & close processing.
874 */
875
Alan Coxa72492b2008-07-22 11:17:05 +0100876static void turn_ints_off(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877{
878 unsigned long flags;
879
880 func_enter();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881 spin_lock_irqsave(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +0100882 (void) sx_in_off(bp, 0); /* Turn off interrupts. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883 spin_unlock_irqrestore(&bp->lock, flags);
884
885 func_exit();
886}
887
Alan Coxa72492b2008-07-22 11:17:05 +0100888static void turn_ints_on(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889{
890 unsigned long flags;
891
892 func_enter();
893
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894 spin_lock_irqsave(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +0100895 (void) sx_in(bp, 0); /* Turn ON interrupts. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896 spin_unlock_irqrestore(&bp->lock, flags);
897
898 func_exit();
899}
900
901
902/* Called with disabled interrupts */
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100903static int sx_setup_board(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904{
905 int error;
906
Jeff Garzikd61780c2005-10-30 15:01:51 -0800907 if (bp->flags & SX_BOARD_ACTIVE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908 return 0;
909
910 if (bp->flags & SX_BOARD_IS_PCI)
Alan Coxa72492b2008-07-22 11:17:05 +0100911 error = request_irq(bp->irq, sx_interrupt,
912 IRQF_DISABLED | IRQF_SHARED, "specialix IO8+", bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913 else
Alan Coxa72492b2008-07-22 11:17:05 +0100914 error = request_irq(bp->irq, sx_interrupt,
915 IRQF_DISABLED, "specialix IO8+", bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916
Jeff Garzikd61780c2005-10-30 15:01:51 -0800917 if (error)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918 return error;
919
Alan Coxa72492b2008-07-22 11:17:05 +0100920 turn_ints_on(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921 bp->flags |= SX_BOARD_ACTIVE;
922
923 return 0;
924}
925
926
927/* Called with disabled interrupts */
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100928static void sx_shutdown_board(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700929{
930 func_enter();
931
932 if (!(bp->flags & SX_BOARD_ACTIVE)) {
933 func_exit();
934 return;
935 }
936
937 bp->flags &= ~SX_BOARD_ACTIVE;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800938
Alan Coxa72492b2008-07-22 11:17:05 +0100939 dprintk(SX_DEBUG_IRQ, "Freeing IRQ%d for board %d.\n",
940 bp->irq, board_No(bp));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941 free_irq(bp->irq, bp);
Alan Coxa72492b2008-07-22 11:17:05 +0100942 turn_ints_off(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700943 func_exit();
944}
945
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100946static unsigned int sx_crtscts(struct tty_struct *tty)
947{
948 if (sx_rtscts)
949 return C_CRTSCTS(tty);
950 return 1;
951}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700952
953/*
Jeff Garzikd61780c2005-10-30 15:01:51 -0800954 * Setting up port characteristics.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955 * Must be called with disabled interrupts
956 */
Alan Coxa72492b2008-07-22 11:17:05 +0100957static void sx_change_speed(struct specialix_board *bp,
958 struct specialix_port *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959{
960 struct tty_struct *tty;
961 unsigned long baud;
962 long tmp;
963 unsigned char cor1 = 0, cor3 = 0;
964 unsigned char mcor1 = 0, mcor2 = 0;
965 static unsigned long again;
966 unsigned long flags;
967
968 func_enter();
969
Alan Coxa72492b2008-07-22 11:17:05 +0100970 tty = port->port.tty;
971 if (!tty || !tty->termios) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972 func_exit();
973 return;
974 }
975
976 port->IER = 0;
977 port->COR2 = 0;
978 /* Select port on the board */
979 spin_lock_irqsave(&bp->lock, flags);
980 sx_out(bp, CD186x_CAR, port_No(port));
981
982 /* The Specialix board doens't implement the RTS lines.
983 They are used to set the IRQ level. Don't touch them. */
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100984 if (sx_crtscts(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985 port->MSVR = MSVR_DTR | (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
986 else
987 port->MSVR = (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
988 spin_unlock_irqrestore(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +0100989 dprintk(SX_DEBUG_TERMIOS, "sx: got MSVR=%02x.\n", port->MSVR);
Alan Cox67cc0162006-09-29 02:01:39 -0700990 baud = tty_get_baud_rate(tty);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800991
Alan Cox67cc0162006-09-29 02:01:39 -0700992 if (baud == 38400) {
Alan Cox44b7d1b2008-07-16 21:57:18 +0100993 if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
Adrian Bunka4bb2cf2006-10-17 00:10:00 -0700994 baud = 57600;
Alan Cox44b7d1b2008-07-16 21:57:18 +0100995 if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
Adrian Bunka4bb2cf2006-10-17 00:10:00 -0700996 baud = 115200;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800998
Alan Cox67cc0162006-09-29 02:01:39 -0700999 if (!baud) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000 /* Drop DTR & exit */
Alan Coxa72492b2008-07-22 11:17:05 +01001001 dprintk(SX_DEBUG_TERMIOS, "Dropping DTR... Hmm....\n");
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001002 if (!sx_crtscts(tty)) {
Alan Coxa72492b2008-07-22 11:17:05 +01001003 port->MSVR &= ~MSVR_DTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004 spin_lock_irqsave(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001005 sx_out(bp, CD186x_MSVR, port->MSVR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001006 spin_unlock_irqrestore(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001007 } else
1008 dprintk(SX_DEBUG_TERMIOS, "Can't drop DTR: no DTR.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001009 return;
1010 } else {
1011 /* Set DTR on */
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001012 if (!sx_crtscts(tty))
Alan Coxa72492b2008-07-22 11:17:05 +01001013 port->MSVR |= MSVR_DTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001014 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001015
Linus Torvalds1da177e2005-04-16 15:20:36 -07001016 /*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001017 * Now we must calculate some speed depended things
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018 */
1019
1020 /* Set baud rate for port */
1021 tmp = port->custom_divisor ;
Alan Coxa72492b2008-07-22 11:17:05 +01001022 if (tmp)
1023 printk(KERN_INFO
1024 "sx%d: Using custom baud rate divisor %ld. \n"
1025 "This is an untested option, please be careful.\n",
1026 port_No(port), tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027 else
Alan Coxa72492b2008-07-22 11:17:05 +01001028 tmp = (((SX_OSCFREQ + baud/2) / baud + CD186x_TPC/2) /
1029 CD186x_TPC);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001030
Alan Coxa72492b2008-07-22 11:17:05 +01001031 if (tmp < 0x10 && time_before(again, jiffies)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032 again = jiffies + HZ * 60;
1033 /* Page 48 of version 2.0 of the CL-CD1865 databook */
1034 if (tmp >= 12) {
Alan Coxa72492b2008-07-22 11:17:05 +01001035 printk(KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
1036 "Performance degradation is possible.\n"
1037 "Read specialix.txt for more info.\n",
1038 port_No(port), tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001039 } else {
Alan Coxa72492b2008-07-22 11:17:05 +01001040 printk(KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
1041 "Warning: overstressing Cirrus chip. This might not work.\n"
1042 "Read specialix.txt for more info.\n", port_No(port), tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043 }
1044 }
1045 spin_lock_irqsave(&bp->lock, flags);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001046 sx_out(bp, CD186x_RBPRH, (tmp >> 8) & 0xff);
1047 sx_out(bp, CD186x_TBPRH, (tmp >> 8) & 0xff);
1048 sx_out(bp, CD186x_RBPRL, tmp & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049 sx_out(bp, CD186x_TBPRL, tmp & 0xff);
1050 spin_unlock_irqrestore(&bp->lock, flags);
Adrian Bunka4bb2cf2006-10-17 00:10:00 -07001051 if (port->custom_divisor)
Alan Coxa72492b2008-07-22 11:17:05 +01001052 baud = (SX_OSCFREQ + port->custom_divisor/2) /
1053 port->custom_divisor;
Adrian Bunka4bb2cf2006-10-17 00:10:00 -07001054 baud = (baud + 5) / 10; /* Estimated CPS */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055
1056 /* Two timer ticks seems enough to wakeup something like SLIP driver */
Jeff Garzikd61780c2005-10-30 15:01:51 -08001057 tmp = ((baud + HZ/2) / HZ) * 2 - CD186x_NFIFO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058 port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ?
1059 SERIAL_XMIT_SIZE - 1 : tmp);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001060
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061 /* Receiver timeout will be transmission time for 1.5 chars */
1062 tmp = (SPECIALIX_TPS + SPECIALIX_TPS/2 + baud/2) / baud;
1063 tmp = (tmp > 0xff) ? 0xff : tmp;
1064 spin_lock_irqsave(&bp->lock, flags);
1065 sx_out(bp, CD186x_RTPR, tmp);
1066 spin_unlock_irqrestore(&bp->lock, flags);
1067 switch (C_CSIZE(tty)) {
Alan Coxa72492b2008-07-22 11:17:05 +01001068 case CS5:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 cor1 |= COR1_5BITS;
1070 break;
Alan Coxa72492b2008-07-22 11:17:05 +01001071 case CS6:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072 cor1 |= COR1_6BITS;
1073 break;
Alan Coxa72492b2008-07-22 11:17:05 +01001074 case CS7:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075 cor1 |= COR1_7BITS;
1076 break;
Alan Coxa72492b2008-07-22 11:17:05 +01001077 case CS8:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078 cor1 |= COR1_8BITS;
1079 break;
1080 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001081
1082 if (C_CSTOPB(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083 cor1 |= COR1_2SB;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001084
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085 cor1 |= COR1_IGNORE;
1086 if (C_PARENB(tty)) {
1087 cor1 |= COR1_NORMPAR;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001088 if (C_PARODD(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089 cor1 |= COR1_ODDP;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001090 if (I_INPCK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091 cor1 &= ~COR1_IGNORE;
1092 }
1093 /* Set marking of some errors */
1094 port->mark_mask = RCSR_OE | RCSR_TOUT;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001095 if (I_INPCK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096 port->mark_mask |= RCSR_FE | RCSR_PE;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001097 if (I_BRKINT(tty) || I_PARMRK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098 port->mark_mask |= RCSR_BREAK;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001099 if (I_IGNPAR(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001100 port->mark_mask &= ~(RCSR_FE | RCSR_PE);
1101 if (I_IGNBRK(tty)) {
1102 port->mark_mask &= ~RCSR_BREAK;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001103 if (I_IGNPAR(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001104 /* Real raw mode. Ignore all */
1105 port->mark_mask &= ~RCSR_OE;
1106 }
1107 /* Enable Hardware Flow Control */
1108 if (C_CRTSCTS(tty)) {
1109#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
1110 port->IER |= IER_DSR | IER_CTS;
1111 mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD;
1112 mcor2 |= MCOR2_DSROD | MCOR2_CTSOD;
1113 spin_lock_irqsave(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001114 tty->hw_stopped = !(sx_in(bp, CD186x_MSVR) &
1115 (MSVR_CTS|MSVR_DSR));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116 spin_unlock_irqrestore(&bp->lock, flags);
1117#else
Jeff Garzikd61780c2005-10-30 15:01:51 -08001118 port->COR2 |= COR2_CTSAE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001119#endif
1120 }
1121 /* Enable Software Flow Control. FIXME: I'm not sure about this */
1122 /* Some people reported that it works, but I still doubt it */
1123 if (I_IXON(tty)) {
1124 port->COR2 |= COR2_TXIBE;
1125 cor3 |= (COR3_FCT | COR3_SCDE);
1126 if (I_IXANY(tty))
1127 port->COR2 |= COR2_IXM;
1128 spin_lock_irqsave(&bp->lock, flags);
1129 sx_out(bp, CD186x_SCHR1, START_CHAR(tty));
1130 sx_out(bp, CD186x_SCHR2, STOP_CHAR(tty));
1131 sx_out(bp, CD186x_SCHR3, START_CHAR(tty));
1132 sx_out(bp, CD186x_SCHR4, STOP_CHAR(tty));
1133 spin_unlock_irqrestore(&bp->lock, flags);
1134 }
1135 if (!C_CLOCAL(tty)) {
1136 /* Enable CD check */
1137 port->IER |= IER_CD;
1138 mcor1 |= MCOR1_CDZD;
1139 mcor2 |= MCOR2_CDOD;
1140 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001141
1142 if (C_CREAD(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143 /* Enable receiver */
1144 port->IER |= IER_RXD;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001145
Linus Torvalds1da177e2005-04-16 15:20:36 -07001146 /* Set input FIFO size (1-8 bytes) */
1147 cor3 |= sx_rxfifo;
1148 /* Setting up CD186x channel registers */
1149 spin_lock_irqsave(&bp->lock, flags);
1150 sx_out(bp, CD186x_COR1, cor1);
1151 sx_out(bp, CD186x_COR2, port->COR2);
1152 sx_out(bp, CD186x_COR3, cor3);
1153 spin_unlock_irqrestore(&bp->lock, flags);
1154 /* Make CD186x know about registers change */
1155 sx_wait_CCR(bp);
1156 spin_lock_irqsave(&bp->lock, flags);
1157 sx_out(bp, CD186x_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3);
1158 /* Setting up modem option registers */
Alan Coxa72492b2008-07-22 11:17:05 +01001159 dprintk(SX_DEBUG_TERMIOS, "Mcor1 = %02x, mcor2 = %02x.\n",
1160 mcor1, mcor2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161 sx_out(bp, CD186x_MCOR1, mcor1);
1162 sx_out(bp, CD186x_MCOR2, mcor2);
1163 spin_unlock_irqrestore(&bp->lock, flags);
1164 /* Enable CD186x transmitter & receiver */
1165 sx_wait_CCR(bp);
1166 spin_lock_irqsave(&bp->lock, flags);
1167 sx_out(bp, CD186x_CCR, CCR_TXEN | CCR_RXEN);
1168 /* Enable interrupts */
1169 sx_out(bp, CD186x_IER, port->IER);
1170 /* And finally set the modem lines... */
1171 sx_out(bp, CD186x_MSVR, port->MSVR);
1172 spin_unlock_irqrestore(&bp->lock, flags);
1173
1174 func_exit();
1175}
1176
1177
1178/* Must be called with interrupts enabled */
Alan Coxa72492b2008-07-22 11:17:05 +01001179static int sx_setup_port(struct specialix_board *bp,
1180 struct specialix_port *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001181{
1182 unsigned long flags;
1183
1184 func_enter();
1185
Alan Cox44b7d1b2008-07-16 21:57:18 +01001186 if (port->port.flags & ASYNC_INITIALIZED) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187 func_exit();
1188 return 0;
1189 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001190
Linus Torvalds1da177e2005-04-16 15:20:36 -07001191 if (!port->xmit_buf) {
1192 /* We may sleep in get_zeroed_page() */
1193 unsigned long tmp;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001194
Alan Coxa72492b2008-07-22 11:17:05 +01001195 tmp = get_zeroed_page(GFP_KERNEL);
1196 if (tmp == 0L) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001197 func_exit();
1198 return -ENOMEM;
1199 }
1200
1201 if (port->xmit_buf) {
1202 free_page(tmp);
1203 func_exit();
1204 return -ERESTARTSYS;
1205 }
1206 port->xmit_buf = (unsigned char *) tmp;
1207 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001208
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209 spin_lock_irqsave(&port->lock, flags);
1210
Alan Cox44b7d1b2008-07-16 21:57:18 +01001211 if (port->port.tty)
1212 clear_bit(TTY_IO_ERROR, &port->port.tty->flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213
1214 port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
1215 sx_change_speed(bp, port);
Alan Cox44b7d1b2008-07-16 21:57:18 +01001216 port->port.flags |= ASYNC_INITIALIZED;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217
1218 spin_unlock_irqrestore(&port->lock, flags);
1219
Jeff Garzikd61780c2005-10-30 15:01:51 -08001220
Linus Torvalds1da177e2005-04-16 15:20:36 -07001221 func_exit();
1222 return 0;
1223}
1224
1225
1226/* Must be called with interrupts disabled */
Alan Coxa72492b2008-07-22 11:17:05 +01001227static void sx_shutdown_port(struct specialix_board *bp,
1228 struct specialix_port *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001229{
1230 struct tty_struct *tty;
1231 int i;
1232 unsigned long flags;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001233
Linus Torvalds1da177e2005-04-16 15:20:36 -07001234 func_enter();
1235
Alan Cox44b7d1b2008-07-16 21:57:18 +01001236 if (!(port->port.flags & ASYNC_INITIALIZED)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001237 func_exit();
1238 return;
1239 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001240
Linus Torvalds1da177e2005-04-16 15:20:36 -07001241 if (sx_debug & SX_DEBUG_FIFO) {
Alan Coxa72492b2008-07-22 11:17:05 +01001242 dprintk(SX_DEBUG_FIFO,
1243 "sx%d: port %d: %ld overruns, FIFO hits [ ",
1244 board_No(bp), port_No(port), port->overrun);
1245 for (i = 0; i < 10; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001246 dprintk(SX_DEBUG_FIFO, "%ld ", port->hits[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247 dprintk(SX_DEBUG_FIFO, "].\n");
1248 }
1249
1250 if (port->xmit_buf) {
1251 free_page((unsigned long) port->xmit_buf);
1252 port->xmit_buf = NULL;
1253 }
1254
1255 /* Select port */
1256 spin_lock_irqsave(&bp->lock, flags);
1257 sx_out(bp, CD186x_CAR, port_No(port));
1258
Alan Coxa72492b2008-07-22 11:17:05 +01001259 tty = port->port.tty;
1260 if (tty == NULL || C_HUPCL(tty)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261 /* Drop DTR */
1262 sx_out(bp, CD186x_MSVDTR, 0);
1263 }
1264 spin_unlock_irqrestore(&bp->lock, flags);
1265 /* Reset port */
1266 sx_wait_CCR(bp);
1267 spin_lock_irqsave(&bp->lock, flags);
1268 sx_out(bp, CD186x_CCR, CCR_SOFTRESET);
1269 /* Disable all interrupts from this port */
1270 port->IER = 0;
1271 sx_out(bp, CD186x_IER, port->IER);
1272 spin_unlock_irqrestore(&bp->lock, flags);
1273 if (tty)
1274 set_bit(TTY_IO_ERROR, &tty->flags);
Alan Cox44b7d1b2008-07-16 21:57:18 +01001275 port->port.flags &= ~ASYNC_INITIALIZED;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001276
1277 if (!bp->count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278 sx_shutdown_board(bp);
1279 func_exit();
1280}
1281
Jeff Garzikd61780c2005-10-30 15:01:51 -08001282
Alan Coxa72492b2008-07-22 11:17:05 +01001283static int block_til_ready(struct tty_struct *tty, struct file *filp,
1284 struct specialix_port *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285{
1286 DECLARE_WAITQUEUE(wait, current);
1287 struct specialix_board *bp = port_Board(port);
1288 int retval;
1289 int do_clocal = 0;
1290 int CD;
1291 unsigned long flags;
1292
1293 func_enter();
1294
1295 /*
1296 * If the device is in the middle of being closed, then block
1297 * until it's done, and then try again.
1298 */
Alan Cox44b7d1b2008-07-16 21:57:18 +01001299 if (tty_hung_up_p(filp) || port->port.flags & ASYNC_CLOSING) {
1300 interruptible_sleep_on(&port->port.close_wait);
1301 if (port->port.flags & ASYNC_HUP_NOTIFY) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302 func_exit();
1303 return -EAGAIN;
1304 } else {
1305 func_exit();
1306 return -ERESTARTSYS;
1307 }
1308 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001309
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310 /*
1311 * If non-blocking mode is set, or the port is not enabled,
1312 * then make the check up front and then exit.
1313 */
1314 if ((filp->f_flags & O_NONBLOCK) ||
1315 (tty->flags & (1 << TTY_IO_ERROR))) {
Alan Cox44b7d1b2008-07-16 21:57:18 +01001316 port->port.flags |= ASYNC_NORMAL_ACTIVE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317 func_exit();
1318 return 0;
1319 }
1320
1321 if (C_CLOCAL(tty))
1322 do_clocal = 1;
1323
1324 /*
1325 * Block waiting for the carrier detect and the line to become
1326 * free (i.e., not in use by the callout). While we are in
1327 * this loop, info->count is dropped by one, so that
1328 * rs_close() knows when to free things. We restore it upon
1329 * exit, either normal or abnormal.
1330 */
1331 retval = 0;
Alan Cox44b7d1b2008-07-16 21:57:18 +01001332 add_wait_queue(&port->port.open_wait, &wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001333 spin_lock_irqsave(&port->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001334 if (!tty_hung_up_p(filp))
Alan Cox44b7d1b2008-07-16 21:57:18 +01001335 port->port.count--;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001336 spin_unlock_irqrestore(&port->lock, flags);
Alan Cox44b7d1b2008-07-16 21:57:18 +01001337 port->port.blocked_open++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001338 while (1) {
1339 spin_lock_irqsave(&bp->lock, flags);
1340 sx_out(bp, CD186x_CAR, port_No(port));
1341 CD = sx_in(bp, CD186x_MSVR) & MSVR_CD;
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001342 if (sx_crtscts(tty)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343 /* Activate RTS */
1344 port->MSVR |= MSVR_DTR; /* WTF? */
Alan Coxa72492b2008-07-22 11:17:05 +01001345 sx_out(bp, CD186x_MSVR, port->MSVR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346 } else {
1347 /* Activate DTR */
1348 port->MSVR |= MSVR_DTR;
Alan Coxa72492b2008-07-22 11:17:05 +01001349 sx_out(bp, CD186x_MSVR, port->MSVR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350 }
1351 spin_unlock_irqrestore(&bp->lock, flags);
1352 set_current_state(TASK_INTERRUPTIBLE);
1353 if (tty_hung_up_p(filp) ||
Alan Cox44b7d1b2008-07-16 21:57:18 +01001354 !(port->port.flags & ASYNC_INITIALIZED)) {
1355 if (port->port.flags & ASYNC_HUP_NOTIFY)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001356 retval = -EAGAIN;
1357 else
Jeff Garzikd61780c2005-10-30 15:01:51 -08001358 retval = -ERESTARTSYS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359 break;
1360 }
Alan Cox44b7d1b2008-07-16 21:57:18 +01001361 if (!(port->port.flags & ASYNC_CLOSING) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362 (do_clocal || CD))
1363 break;
1364 if (signal_pending(current)) {
1365 retval = -ERESTARTSYS;
1366 break;
1367 }
Arnd Bergmanne142a312010-06-01 22:53:10 +02001368 tty_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001369 schedule();
Arnd Bergmanne142a312010-06-01 22:53:10 +02001370 tty_lock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371 }
1372
1373 set_current_state(TASK_RUNNING);
Alan Cox44b7d1b2008-07-16 21:57:18 +01001374 remove_wait_queue(&port->port.open_wait, &wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001375 spin_lock_irqsave(&port->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001376 if (!tty_hung_up_p(filp))
Alan Cox44b7d1b2008-07-16 21:57:18 +01001377 port->port.count++;
Alan Cox44b7d1b2008-07-16 21:57:18 +01001378 port->port.blocked_open--;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379 spin_unlock_irqrestore(&port->lock, flags);
1380 if (retval) {
1381 func_exit();
1382 return retval;
1383 }
1384
Alan Cox44b7d1b2008-07-16 21:57:18 +01001385 port->port.flags |= ASYNC_NORMAL_ACTIVE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001386 func_exit();
1387 return 0;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001388}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389
1390
Alan Coxa72492b2008-07-22 11:17:05 +01001391static int sx_open(struct tty_struct *tty, struct file *filp)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001392{
1393 int board;
1394 int error;
Alan Coxa72492b2008-07-22 11:17:05 +01001395 struct specialix_port *port;
1396 struct specialix_board *bp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001397 int i;
1398 unsigned long flags;
1399
1400 func_enter();
1401
1402 board = SX_BOARD(tty->index);
1403
1404 if (board >= SX_NBOARD || !(sx_board[board].flags & SX_BOARD_PRESENT)) {
1405 func_exit();
1406 return -ENODEV;
1407 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001408
Linus Torvalds1da177e2005-04-16 15:20:36 -07001409 bp = &sx_board[board];
1410 port = sx_port + board * SX_NPORT + SX_PORT(tty->index);
1411 port->overrun = 0;
1412 for (i = 0; i < 10; i++)
Alan Coxa72492b2008-07-22 11:17:05 +01001413 port->hits[i] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001414
Alan Coxa72492b2008-07-22 11:17:05 +01001415 dprintk(SX_DEBUG_OPEN,
1416 "Board = %d, bp = %p, port = %p, portno = %d.\n",
1417 board, bp, port, SX_PORT(tty->index));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001418
1419 if (sx_paranoia_check(port, tty->name, "sx_open")) {
1420 func_enter();
1421 return -ENODEV;
1422 }
1423
Alan Coxa72492b2008-07-22 11:17:05 +01001424 error = sx_setup_board(bp);
1425 if (error) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001426 func_exit();
1427 return error;
1428 }
1429
1430 spin_lock_irqsave(&bp->lock, flags);
Alan Cox44b7d1b2008-07-16 21:57:18 +01001431 port->port.count++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001432 bp->count++;
1433 tty->driver_data = port;
Alan Cox44b7d1b2008-07-16 21:57:18 +01001434 port->port.tty = tty;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001435 spin_unlock_irqrestore(&bp->lock, flags);
1436
Alan Coxa72492b2008-07-22 11:17:05 +01001437 error = sx_setup_port(bp, port);
1438 if (error) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001439 func_enter();
1440 return error;
1441 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001442
Alan Coxa72492b2008-07-22 11:17:05 +01001443 error = block_til_ready(tty, filp, port);
1444 if (error) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001445 func_enter();
1446 return error;
1447 }
1448
1449 func_exit();
1450 return 0;
1451}
1452
Alan Cox978e5952008-04-30 00:53:59 -07001453static void sx_flush_buffer(struct tty_struct *tty)
1454{
Alan Coxc9f19e92009-01-02 13:47:26 +00001455 struct specialix_port *port = tty->driver_data;
Alan Cox978e5952008-04-30 00:53:59 -07001456 unsigned long flags;
Alan Coxa72492b2008-07-22 11:17:05 +01001457 struct specialix_board *bp;
Alan Cox978e5952008-04-30 00:53:59 -07001458
1459 func_enter();
1460
1461 if (sx_paranoia_check(port, tty->name, "sx_flush_buffer")) {
1462 func_exit();
1463 return;
1464 }
1465
1466 bp = port_Board(port);
1467 spin_lock_irqsave(&port->lock, flags);
1468 port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
1469 spin_unlock_irqrestore(&port->lock, flags);
1470 tty_wakeup(tty);
1471
1472 func_exit();
1473}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001474
Alan Coxa72492b2008-07-22 11:17:05 +01001475static void sx_close(struct tty_struct *tty, struct file *filp)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001476{
Alan Coxc9f19e92009-01-02 13:47:26 +00001477 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001478 struct specialix_board *bp;
1479 unsigned long flags;
1480 unsigned long timeout;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001481
Linus Torvalds1da177e2005-04-16 15:20:36 -07001482 func_enter();
1483 if (!port || sx_paranoia_check(port, tty->name, "close")) {
1484 func_exit();
1485 return;
1486 }
1487 spin_lock_irqsave(&port->lock, flags);
1488
1489 if (tty_hung_up_p(filp)) {
1490 spin_unlock_irqrestore(&port->lock, flags);
1491 func_exit();
1492 return;
1493 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001494
Linus Torvalds1da177e2005-04-16 15:20:36 -07001495 bp = port_Board(port);
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001496 if (tty->count == 1 && port->port.count != 1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001497 printk(KERN_ERR "sx%d: sx_close: bad port count;"
1498 " tty->count is 1, port count is %d\n",
Alan Cox44b7d1b2008-07-16 21:57:18 +01001499 board_No(bp), port->port.count);
1500 port->port.count = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001501 }
1502
Alan Cox44b7d1b2008-07-16 21:57:18 +01001503 if (port->port.count > 1) {
1504 port->port.count--;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001505 bp->count--;
1506
1507 spin_unlock_irqrestore(&port->lock, flags);
1508
1509 func_exit();
1510 return;
1511 }
Alan Cox44b7d1b2008-07-16 21:57:18 +01001512 port->port.flags |= ASYNC_CLOSING;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001513 /*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001514 * Now we wait for the transmit buffer to clear; and we notify
Linus Torvalds1da177e2005-04-16 15:20:36 -07001515 * the line discipline to only process XON/XOFF characters.
1516 */
1517 tty->closing = 1;
1518 spin_unlock_irqrestore(&port->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001519 dprintk(SX_DEBUG_OPEN, "Closing\n");
1520 if (port->port.closing_wait != ASYNC_CLOSING_WAIT_NONE)
Alan Cox44b7d1b2008-07-16 21:57:18 +01001521 tty_wait_until_sent(tty, port->port.closing_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522 /*
1523 * At this point we stop accepting input. To do this, we
1524 * disable the receive line status interrupts, and tell the
1525 * interrupt driver to stop checking the data ready bit in the
1526 * line status register.
1527 */
Alan Coxa72492b2008-07-22 11:17:05 +01001528 dprintk(SX_DEBUG_OPEN, "Closed\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529 port->IER &= ~IER_RXD;
Alan Cox44b7d1b2008-07-16 21:57:18 +01001530 if (port->port.flags & ASYNC_INITIALIZED) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001531 port->IER &= ~IER_TXRDY;
1532 port->IER |= IER_TXEMPTY;
1533 spin_lock_irqsave(&bp->lock, flags);
1534 sx_out(bp, CD186x_CAR, port_No(port));
1535 sx_out(bp, CD186x_IER, port->IER);
1536 spin_unlock_irqrestore(&bp->lock, flags);
1537 /*
1538 * Before we drop DTR, make sure the UART transmitter
1539 * has completely drained; this is especially
1540 * important if there is a transmit FIFO!
1541 */
1542 timeout = jiffies+HZ;
Alan Coxa72492b2008-07-22 11:17:05 +01001543 while (port->IER & IER_TXEMPTY) {
1544 set_current_state(TASK_INTERRUPTIBLE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001545 msleep_interruptible(jiffies_to_msecs(port->timeout));
1546 if (time_after(jiffies, timeout)) {
Alan Coxa72492b2008-07-22 11:17:05 +01001547 printk(KERN_INFO "Timeout waiting for close\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001548 break;
1549 }
1550 }
1551
1552 }
1553
1554 if (--bp->count < 0) {
Alan Coxa72492b2008-07-22 11:17:05 +01001555 printk(KERN_ERR
1556 "sx%d: sx_shutdown_port: bad board count: %d port: %d\n",
1557 board_No(bp), bp->count, tty->index);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001558 bp->count = 0;
1559 }
Alan Cox44b7d1b2008-07-16 21:57:18 +01001560 if (--port->port.count < 0) {
Alan Coxa72492b2008-07-22 11:17:05 +01001561 printk(KERN_ERR
1562 "sx%d: sx_close: bad port count for tty%d: %d\n",
1563 board_No(bp), port_No(port), port->port.count);
Alan Cox44b7d1b2008-07-16 21:57:18 +01001564 port->port.count = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001565 }
1566
1567 sx_shutdown_port(bp, port);
Alan Cox978e5952008-04-30 00:53:59 -07001568 sx_flush_buffer(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001569 tty_ldisc_flush(tty);
1570 spin_lock_irqsave(&port->lock, flags);
1571 tty->closing = 0;
Alan Cox44b7d1b2008-07-16 21:57:18 +01001572 port->port.tty = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001573 spin_unlock_irqrestore(&port->lock, flags);
Alan Cox44b7d1b2008-07-16 21:57:18 +01001574 if (port->port.blocked_open) {
Alan Coxa72492b2008-07-22 11:17:05 +01001575 if (port->port.close_delay)
1576 msleep_interruptible(
1577 jiffies_to_msecs(port->port.close_delay));
Alan Cox44b7d1b2008-07-16 21:57:18 +01001578 wake_up_interruptible(&port->port.open_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001579 }
Alan Cox44b7d1b2008-07-16 21:57:18 +01001580 port->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
1581 wake_up_interruptible(&port->port.close_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001582
1583 func_exit();
1584}
1585
1586
Alan Coxa72492b2008-07-22 11:17:05 +01001587static int sx_write(struct tty_struct *tty,
1588 const unsigned char *buf, int count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001589{
Alan Coxc9f19e92009-01-02 13:47:26 +00001590 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001591 struct specialix_board *bp;
1592 int c, total = 0;
1593 unsigned long flags;
1594
1595 func_enter();
1596 if (sx_paranoia_check(port, tty->name, "sx_write")) {
1597 func_exit();
1598 return 0;
1599 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001600
Linus Torvalds1da177e2005-04-16 15:20:36 -07001601 bp = port_Board(port);
1602
Jiri Slaby365e0222006-09-30 23:28:11 -07001603 if (!port->xmit_buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001604 func_exit();
1605 return 0;
1606 }
1607
1608 while (1) {
1609 spin_lock_irqsave(&port->lock, flags);
1610 c = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
1611 SERIAL_XMIT_SIZE - port->xmit_head));
1612 if (c <= 0) {
1613 spin_unlock_irqrestore(&port->lock, flags);
1614 break;
1615 }
1616 memcpy(port->xmit_buf + port->xmit_head, buf, c);
1617 port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
1618 port->xmit_cnt += c;
1619 spin_unlock_irqrestore(&port->lock, flags);
1620
1621 buf += c;
1622 count -= c;
1623 total += c;
1624 }
1625
1626 spin_lock_irqsave(&bp->lock, flags);
1627 if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
1628 !(port->IER & IER_TXRDY)) {
1629 port->IER |= IER_TXRDY;
1630 sx_out(bp, CD186x_CAR, port_No(port));
1631 sx_out(bp, CD186x_IER, port->IER);
1632 }
1633 spin_unlock_irqrestore(&bp->lock, flags);
1634 func_exit();
1635
1636 return total;
1637}
1638
1639
Alan Coxa72492b2008-07-22 11:17:05 +01001640static int sx_put_char(struct tty_struct *tty, unsigned char ch)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641{
Alan Coxc9f19e92009-01-02 13:47:26 +00001642 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001643 unsigned long flags;
Alan Coxa72492b2008-07-22 11:17:05 +01001644 struct specialix_board *bp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001645
1646 func_enter();
1647
1648 if (sx_paranoia_check(port, tty->name, "sx_put_char")) {
1649 func_exit();
Alan Cox6ae045762008-04-30 00:54:07 -07001650 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001651 }
Alan Coxa72492b2008-07-22 11:17:05 +01001652 dprintk(SX_DEBUG_TX, "check tty: %p %p\n", tty, port->xmit_buf);
Eric Sesterhenn326f28e92006-06-25 05:48:48 -07001653 if (!port->xmit_buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001654 func_exit();
Alan Cox6ae045762008-04-30 00:54:07 -07001655 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656 }
1657 bp = port_Board(port);
1658 spin_lock_irqsave(&port->lock, flags);
1659
Alan Coxa72492b2008-07-22 11:17:05 +01001660 dprintk(SX_DEBUG_TX, "xmit_cnt: %d xmit_buf: %p\n",
1661 port->xmit_cnt, port->xmit_buf);
1662 if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1 || !port->xmit_buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663 spin_unlock_irqrestore(&port->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001664 dprintk(SX_DEBUG_TX, "Exit size\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001665 func_exit();
Alan Cox6ae045762008-04-30 00:54:07 -07001666 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667 }
Alan Coxa72492b2008-07-22 11:17:05 +01001668 dprintk(SX_DEBUG_TX, "Handle xmit: %p %p\n", port, port->xmit_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001669 port->xmit_buf[port->xmit_head++] = ch;
1670 port->xmit_head &= SERIAL_XMIT_SIZE - 1;
1671 port->xmit_cnt++;
1672 spin_unlock_irqrestore(&port->lock, flags);
1673
1674 func_exit();
Alan Cox6ae045762008-04-30 00:54:07 -07001675 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001676}
1677
1678
Alan Coxa72492b2008-07-22 11:17:05 +01001679static void sx_flush_chars(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001680{
Alan Coxc9f19e92009-01-02 13:47:26 +00001681 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001682 unsigned long flags;
Alan Coxa72492b2008-07-22 11:17:05 +01001683 struct specialix_board *bp = port_Board(port);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001684
1685 func_enter();
1686
1687 if (sx_paranoia_check(port, tty->name, "sx_flush_chars")) {
1688 func_exit();
1689 return;
1690 }
1691 if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
1692 !port->xmit_buf) {
1693 func_exit();
1694 return;
1695 }
1696 spin_lock_irqsave(&bp->lock, flags);
1697 port->IER |= IER_TXRDY;
1698 sx_out(port_Board(port), CD186x_CAR, port_No(port));
1699 sx_out(port_Board(port), CD186x_IER, port->IER);
1700 spin_unlock_irqrestore(&bp->lock, flags);
1701
1702 func_exit();
1703}
1704
1705
Alan Coxa72492b2008-07-22 11:17:05 +01001706static int sx_write_room(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001707{
Alan Coxc9f19e92009-01-02 13:47:26 +00001708 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001709 int ret;
1710
1711 func_enter();
1712
1713 if (sx_paranoia_check(port, tty->name, "sx_write_room")) {
1714 func_exit();
1715 return 0;
1716 }
1717
1718 ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;
1719 if (ret < 0)
1720 ret = 0;
1721
1722 func_exit();
1723 return ret;
1724}
1725
1726
1727static int sx_chars_in_buffer(struct tty_struct *tty)
1728{
Alan Coxc9f19e92009-01-02 13:47:26 +00001729 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001730
1731 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08001732
Linus Torvalds1da177e2005-04-16 15:20:36 -07001733 if (sx_paranoia_check(port, tty->name, "sx_chars_in_buffer")) {
1734 func_exit();
1735 return 0;
1736 }
1737 func_exit();
1738 return port->xmit_cnt;
1739}
1740
Linus Torvalds1da177e2005-04-16 15:20:36 -07001741static int sx_tiocmget(struct tty_struct *tty, struct file *file)
1742{
Alan Coxc9f19e92009-01-02 13:47:26 +00001743 struct specialix_port *port = tty->driver_data;
Alan Coxa72492b2008-07-22 11:17:05 +01001744 struct specialix_board *bp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001745 unsigned char status;
1746 unsigned int result;
1747 unsigned long flags;
1748
1749 func_enter();
1750
Harvey Harrisonbf9d8922008-04-30 00:55:10 -07001751 if (sx_paranoia_check(port, tty->name, __func__)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001752 func_exit();
1753 return -ENODEV;
1754 }
1755
1756 bp = port_Board(port);
Alan Coxa72492b2008-07-22 11:17:05 +01001757 spin_lock_irqsave(&bp->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001758 sx_out(bp, CD186x_CAR, port_No(port));
1759 status = sx_in(bp, CD186x_MSVR);
1760 spin_unlock_irqrestore(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001761 dprintk(SX_DEBUG_INIT, "Got msvr[%d] = %02x, car = %d.\n",
1762 port_No(port), status, sx_in(bp, CD186x_CAR));
1763 dprintk(SX_DEBUG_INIT, "sx_port = %p, port = %p\n", sx_port, port);
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001764 if (sx_crtscts(port->port.tty)) {
1765 result = TIOCM_DTR | TIOCM_DSR
Alan Coxa72492b2008-07-22 11:17:05 +01001766 | ((status & MSVR_DTR) ? TIOCM_RTS : 0)
1767 | ((status & MSVR_CD) ? TIOCM_CAR : 0)
Alan Coxa72492b2008-07-22 11:17:05 +01001768 | ((status & MSVR_CTS) ? TIOCM_CTS : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001769 } else {
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001770 result = TIOCM_RTS | TIOCM_DSR
Alan Coxa72492b2008-07-22 11:17:05 +01001771 | ((status & MSVR_DTR) ? TIOCM_DTR : 0)
1772 | ((status & MSVR_CD) ? TIOCM_CAR : 0)
Alan Coxa72492b2008-07-22 11:17:05 +01001773 | ((status & MSVR_CTS) ? TIOCM_CTS : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001774 }
1775
1776 func_exit();
1777
1778 return result;
1779}
1780
1781
1782static int sx_tiocmset(struct tty_struct *tty, struct file *file,
1783 unsigned int set, unsigned int clear)
1784{
Alan Coxc9f19e92009-01-02 13:47:26 +00001785 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001786 unsigned long flags;
1787 struct specialix_board *bp;
1788
1789 func_enter();
1790
Harvey Harrisonbf9d8922008-04-30 00:55:10 -07001791 if (sx_paranoia_check(port, tty->name, __func__)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001792 func_exit();
1793 return -ENODEV;
1794 }
1795
1796 bp = port_Board(port);
1797
1798 spin_lock_irqsave(&port->lock, flags);
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001799 if (sx_crtscts(port->port.tty)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001800 if (set & TIOCM_RTS)
1801 port->MSVR |= MSVR_DTR;
1802 } else {
1803 if (set & TIOCM_DTR)
1804 port->MSVR |= MSVR_DTR;
1805 }
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001806 if (sx_crtscts(port->port.tty)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001807 if (clear & TIOCM_RTS)
1808 port->MSVR &= ~MSVR_DTR;
1809 } else {
1810 if (clear & TIOCM_DTR)
1811 port->MSVR &= ~MSVR_DTR;
1812 }
Julia Lawall25470252009-07-20 16:05:07 +01001813 spin_lock(&bp->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001814 sx_out(bp, CD186x_CAR, port_No(port));
1815 sx_out(bp, CD186x_MSVR, port->MSVR);
Julia Lawall25470252009-07-20 16:05:07 +01001816 spin_unlock(&bp->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001817 spin_unlock_irqrestore(&port->lock, flags);
1818 func_exit();
1819 return 0;
1820}
1821
1822
Alan Coxfaa76122008-07-22 11:19:05 +01001823static int sx_send_break(struct tty_struct *tty, int length)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001824{
Alan Coxc9f19e92009-01-02 13:47:26 +00001825 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001826 struct specialix_board *bp = port_Board(port);
1827 unsigned long flags;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001828
Linus Torvalds1da177e2005-04-16 15:20:36 -07001829 func_enter();
Alan Coxfaa76122008-07-22 11:19:05 +01001830 if (length == 0 || length == -1)
1831 return -EOPNOTSUPP;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001832
Alan Coxa72492b2008-07-22 11:17:05 +01001833 spin_lock_irqsave(&port->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001834 port->break_length = SPECIALIX_TPS / HZ * length;
1835 port->COR2 |= COR2_ETC;
1836 port->IER |= IER_TXRDY;
Julia Lawall25470252009-07-20 16:05:07 +01001837 spin_lock(&bp->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001838 sx_out(bp, CD186x_CAR, port_No(port));
1839 sx_out(bp, CD186x_COR2, port->COR2);
1840 sx_out(bp, CD186x_IER, port->IER);
Julia Lawall25470252009-07-20 16:05:07 +01001841 spin_unlock(&bp->lock);
Alan Coxa72492b2008-07-22 11:17:05 +01001842 spin_unlock_irqrestore(&port->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001843 sx_wait_CCR(bp);
1844 spin_lock_irqsave(&bp->lock, flags);
1845 sx_out(bp, CD186x_CCR, CCR_CORCHG2);
1846 spin_unlock_irqrestore(&bp->lock, flags);
1847 sx_wait_CCR(bp);
1848
1849 func_exit();
Alan Coxfaa76122008-07-22 11:19:05 +01001850 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001851}
1852
1853
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001854static int sx_set_serial_info(struct specialix_port *port,
Alan Coxa72492b2008-07-22 11:17:05 +01001855 struct serial_struct __user *newinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001856{
1857 struct serial_struct tmp;
1858 struct specialix_board *bp = port_Board(port);
1859 int change_speed;
1860
1861 func_enter();
Alan Coxb190e172008-04-30 00:53:22 -07001862
Linus Torvalds1da177e2005-04-16 15:20:36 -07001863 if (copy_from_user(&tmp, newinfo, sizeof(tmp))) {
1864 func_enter();
1865 return -EFAULT;
1866 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001867
Alan Cox7479db02010-06-01 22:52:49 +02001868 mutex_lock(&port->port.mutex);
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 Cox7479db02010-06-01 22:52:49 +02001879 mutex_unlock(&port->port.mutex);
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 Cox7479db02010-06-01 22:52:49 +02001896 mutex_unlock(&port->port.mutex);
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 Cox7479db02010-06-01 22:52:49 +02001910 mutex_lock(&port->port.mutex);
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 Cox7479db02010-06-01 22:52:49 +02001921 mutex_unlock(&port->port.mutex);
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 Short76910302006-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);