blob: e72be4190a442a6378cca9a68a306b3421dcb06c [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>
90#include <linux/fcntl.h>
91#include <linux/major.h>
92#include <linux/delay.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070093#include <linux/pci.h>
94#include <linux/init.h>
Alan Coxa72492b2008-07-22 11:17:05 +010095#include <linux/uaccess.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070096
97#include "specialix_io8.h"
98#include "cd1865.h"
99
100
101/*
102 This driver can spew a whole lot of debugging output at you. If you
103 need maximum performance, you should disable the DEBUG define. To
104 aid in debugging in the field, I'm leaving the compile-time debug
105 features enabled, and disable them "runtime". That allows me to
106 instruct people with problems to enable debugging without requiring
107 them to recompile...
108*/
109#define DEBUG
110
111static int sx_debug;
112static int sx_rxfifo = SPECIALIX_RXFIFO;
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100113static int sx_rtscts;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114
115#ifdef DEBUG
Alan Coxa72492b2008-07-22 11:17:05 +0100116#define dprintk(f, str...) if (sx_debug & f) printk(str)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117#else
118#define dprintk(f, str...) /* nothing */
119#endif
120
121#define SX_DEBUG_FLOW 0x0001
122#define SX_DEBUG_DATA 0x0002
123#define SX_DEBUG_PROBE 0x0004
124#define SX_DEBUG_CHAN 0x0008
125#define SX_DEBUG_INIT 0x0010
126#define SX_DEBUG_RX 0x0020
127#define SX_DEBUG_TX 0x0040
128#define SX_DEBUG_IRQ 0x0080
129#define SX_DEBUG_OPEN 0x0100
130#define SX_DEBUG_TERMIOS 0x0200
131#define SX_DEBUG_SIGNALS 0x0400
132#define SX_DEBUG_FIFO 0x0800
133
134
Alan Coxa72492b2008-07-22 11:17:05 +0100135#define func_enter() dprintk(SX_DEBUG_FLOW, "io8: enter %s\n", __func__)
136#define func_exit() dprintk(SX_DEBUG_FLOW, "io8: exit %s\n", __func__)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138
139/* Configurable options: */
140
141/* Am I paranoid or not ? ;-) */
142#define SPECIALIX_PARANOIA_CHECK
143
Jeff Garzikd61780c2005-10-30 15:01:51 -0800144/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 * The following defines are mostly for testing purposes. But if you need
146 * some nice reporting in your syslog, you can define them also.
147 */
148#undef SX_REPORT_FIFO
149#undef SX_REPORT_OVERRUN
150
151
152
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153
154#define SPECIALIX_LEGAL_FLAGS \
155 (ASYNC_HUP_NOTIFY | ASYNC_SAK | ASYNC_SPLIT_TERMIOS | \
156 ASYNC_SPD_HI | ASYNC_SPEED_VHI | ASYNC_SESSION_LOCKOUT | \
157 ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP)
158
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159static struct tty_driver *specialix_driver;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161static struct specialix_board sx_board[SX_NBOARD] = {
162 { 0, SX_IOBASE1, 9, },
163 { 0, SX_IOBASE2, 11, },
164 { 0, SX_IOBASE3, 12, },
165 { 0, SX_IOBASE4, 15, },
166};
167
168static struct specialix_port sx_port[SX_NBOARD * SX_NPORT];
169
170
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100171static int sx_paranoia_check(struct specialix_port const *port,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 char *name, const char *routine)
173{
174#ifdef SPECIALIX_PARANOIA_CHECK
Alan Coxa72492b2008-07-22 11:17:05 +0100175 static const char *badmagic = KERN_ERR
176 "sx: Warning: bad specialix port magic number for device %s in %s\n";
177 static const char *badinfo = KERN_ERR
178 "sx: Warning: null specialix port for device %s in %s\n";
Jeff Garzikd61780c2005-10-30 15:01:51 -0800179
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180 if (!port) {
181 printk(badinfo, name, routine);
182 return 1;
183 }
184 if (port->magic != SPECIALIX_MAGIC) {
185 printk(badmagic, name, routine);
186 return 1;
187 }
188#endif
189 return 0;
190}
191
192
193/*
Jeff Garzikd61780c2005-10-30 15:01:51 -0800194 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195 * Service functions for specialix IO8+ driver.
Jeff Garzikd61780c2005-10-30 15:01:51 -0800196 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197 */
198
199/* Get board number from pointer */
Alan Coxa72492b2008-07-22 11:17:05 +0100200static inline int board_No(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201{
202 return bp - sx_board;
203}
204
205
206/* Get port number from pointer */
Alan Coxa72492b2008-07-22 11:17:05 +0100207static inline int port_No(struct specialix_port const *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208{
Jeff Garzikd61780c2005-10-30 15:01:51 -0800209 return SX_PORT(port - sx_port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210}
211
212
213/* Get pointer to board from pointer to port */
Alan Coxa72492b2008-07-22 11:17:05 +0100214static inline struct specialix_board *port_Board(
215 struct specialix_port const *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216{
217 return &sx_board[SX_BOARD(port - sx_port)];
218}
219
220
221/* Input Byte from CL CD186x register */
Alan Coxa72492b2008-07-22 11:17:05 +0100222static inline unsigned char sx_in(struct specialix_board *bp,
223 unsigned short reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224{
225 bp->reg = reg | 0x80;
Alan Coxa72492b2008-07-22 11:17:05 +0100226 outb(reg | 0x80, bp->base + SX_ADDR_REG);
227 return inb(bp->base + SX_DATA_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228}
229
230
231/* Output Byte to CL CD186x register */
Alan Coxa72492b2008-07-22 11:17:05 +0100232static inline void sx_out(struct specialix_board *bp, unsigned short reg,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233 unsigned char val)
234{
235 bp->reg = reg | 0x80;
Alan Coxa72492b2008-07-22 11:17:05 +0100236 outb(reg | 0x80, bp->base + SX_ADDR_REG);
237 outb(val, bp->base + SX_DATA_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238}
239
240
241/* Input Byte from CL CD186x register */
Alan Coxa72492b2008-07-22 11:17:05 +0100242static inline unsigned char sx_in_off(struct specialix_board *bp,
243 unsigned short reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244{
245 bp->reg = reg;
Alan Coxa72492b2008-07-22 11:17:05 +0100246 outb(reg, bp->base + SX_ADDR_REG);
247 return inb(bp->base + SX_DATA_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248}
249
250
251/* Output Byte to CL CD186x register */
Alan Coxa72492b2008-07-22 11:17:05 +0100252static inline void sx_out_off(struct specialix_board *bp,
253 unsigned short reg, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254{
255 bp->reg = reg;
Alan Coxa72492b2008-07-22 11:17:05 +0100256 outb(reg, bp->base + SX_ADDR_REG);
257 outb(val, bp->base + SX_DATA_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258}
259
260
261/* Wait for Channel Command Register ready */
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100262static void sx_wait_CCR(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263{
264 unsigned long delay, flags;
265 unsigned char ccr;
266
267 for (delay = SX_CCR_TIMEOUT; delay; delay--) {
268 spin_lock_irqsave(&bp->lock, flags);
269 ccr = sx_in(bp, CD186x_CCR);
270 spin_unlock_irqrestore(&bp->lock, flags);
271 if (!ccr)
272 return;
Alan Coxa72492b2008-07-22 11:17:05 +0100273 udelay(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800275
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276 printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp));
277}
278
279
280/* Wait for Channel Command Register ready */
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100281static void sx_wait_CCR_off(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282{
283 unsigned long delay;
284 unsigned char crr;
285 unsigned long flags;
286
287 for (delay = SX_CCR_TIMEOUT; delay; delay--) {
288 spin_lock_irqsave(&bp->lock, flags);
289 crr = sx_in_off(bp, CD186x_CCR);
290 spin_unlock_irqrestore(&bp->lock, flags);
291 if (!crr)
292 return;
Alan Coxa72492b2008-07-22 11:17:05 +0100293 udelay(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800295
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296 printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp));
297}
298
299
300/*
301 * specialix IO8+ IO range functions.
302 */
303
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100304static int sx_request_io_range(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305{
Jeff Garzikd61780c2005-10-30 15:01:51 -0800306 return request_region(bp->base,
307 bp->flags & SX_BOARD_IS_PCI ? SX_PCI_IO_SPACE : SX_IO_SPACE,
308 "specialix IO8+") == NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309}
310
311
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100312static void sx_release_io_range(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313{
Alan Coxa72492b2008-07-22 11:17:05 +0100314 release_region(bp->base, bp->flags & SX_BOARD_IS_PCI ?
315 SX_PCI_IO_SPACE : SX_IO_SPACE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316}
317
Jeff Garzikd61780c2005-10-30 15:01:51 -0800318
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319/* Set the IRQ using the RTS lines that run to the PAL on the board.... */
Alan Coxa72492b2008-07-22 11:17:05 +0100320static int sx_set_irq(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321{
322 int virq;
323 int i;
324 unsigned long flags;
325
Jeff Garzikd61780c2005-10-30 15:01:51 -0800326 if (bp->flags & SX_BOARD_IS_PCI)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327 return 1;
328 switch (bp->irq) {
329 /* In the same order as in the docs... */
Alan Coxa72492b2008-07-22 11:17:05 +0100330 case 15:
331 virq = 0;
332 break;
333 case 12:
334 virq = 1;
335 break;
336 case 11:
337 virq = 2;
338 break;
339 case 9:
340 virq = 3;
341 break;
342 default:printk(KERN_ERR
343 "Speclialix: cannot set irq to %d.\n", bp->irq);
344 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345 }
346 spin_lock_irqsave(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +0100347 for (i = 0; i < 2; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348 sx_out(bp, CD186x_CAR, i);
349 sx_out(bp, CD186x_MSVRTS, ((virq >> i) & 0x1)? MSVR_RTS:0);
350 }
351 spin_unlock_irqrestore(&bp->lock, flags);
352 return 1;
353}
354
355
356/* Reset and setup CD186x chip */
Alan Coxa72492b2008-07-22 11:17:05 +0100357static int sx_init_CD186x(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358{
359 unsigned long flags;
360 int scaler;
361 int rv = 1;
362
363 func_enter();
364 sx_wait_CCR_off(bp); /* Wait for CCR ready */
365 spin_lock_irqsave(&bp->lock, flags);
366 sx_out_off(bp, CD186x_CCR, CCR_HARDRESET); /* Reset CD186x chip */
367 spin_unlock_irqrestore(&bp->lock, flags);
Jiri Slaby3e98cee2007-07-17 04:05:19 -0700368 msleep(50); /* Delay 0.05 sec */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369 spin_lock_irqsave(&bp->lock, flags);
370 sx_out_off(bp, CD186x_GIVR, SX_ID); /* Set ID for this chip */
371 sx_out_off(bp, CD186x_GICR, 0); /* Clear all bits */
372 sx_out_off(bp, CD186x_PILR1, SX_ACK_MINT); /* Prio for modem intr */
373 sx_out_off(bp, CD186x_PILR2, SX_ACK_TINT); /* Prio for transmitter intr */
374 sx_out_off(bp, CD186x_PILR3, SX_ACK_RINT); /* Prio for receiver intr */
375 /* Set RegAckEn */
Alan Coxa72492b2008-07-22 11:17:05 +0100376 sx_out_off(bp, CD186x_SRCR, sx_in(bp, CD186x_SRCR) | SRCR_REGACKEN);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800377
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378 /* Setting up prescaler. We need 4 ticks per 1 ms */
379 scaler = SX_OSCFREQ/SPECIALIX_TPS;
380
381 sx_out_off(bp, CD186x_PPRH, scaler >> 8);
382 sx_out_off(bp, CD186x_PPRL, scaler & 0xff);
383 spin_unlock_irqrestore(&bp->lock, flags);
384
Alan Coxa72492b2008-07-22 11:17:05 +0100385 if (!sx_set_irq(bp)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386 /* Figure out how to pass this along... */
Alan Coxa72492b2008-07-22 11:17:05 +0100387 printk(KERN_ERR "Cannot set irq to %d.\n", bp->irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 rv = 0;
389 }
390
391 func_exit();
392 return rv;
393}
394
395
Alan Coxa72492b2008-07-22 11:17:05 +0100396static int read_cross_byte(struct specialix_board *bp, int reg, int bit)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397{
398 int i;
399 int t;
400 unsigned long flags;
401
402 spin_lock_irqsave(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +0100403 for (i = 0, t = 0; i < 8; i++) {
404 sx_out_off(bp, CD186x_CAR, i);
405 if (sx_in_off(bp, reg) & bit)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406 t |= 1 << i;
407 }
408 spin_unlock_irqrestore(&bp->lock, flags);
409
410 return t;
411}
412
413
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414/* Main probing routine, also sets irq. */
415static int sx_probe(struct specialix_board *bp)
416{
417 unsigned char val1, val2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 int rev;
419 int chip;
420
421 func_enter();
422
Jeff Garzikd61780c2005-10-30 15:01:51 -0800423 if (sx_request_io_range(bp)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424 func_exit();
425 return 1;
426 }
427
428 /* Are the I/O ports here ? */
429 sx_out_off(bp, CD186x_PPRL, 0x5a);
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100430 udelay(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431 val1 = sx_in_off(bp, CD186x_PPRL);
432
433 sx_out_off(bp, CD186x_PPRL, 0xa5);
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100434 udelay(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435 val2 = sx_in_off(bp, CD186x_PPRL);
436
Jeff Garzikd61780c2005-10-30 15:01:51 -0800437
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100438 if (val1 != 0x5a || val2 != 0xa5) {
Alan Coxa72492b2008-07-22 11:17:05 +0100439 printk(KERN_INFO
440 "sx%d: specialix IO8+ Board at 0x%03x not found.\n",
441 board_No(bp), bp->base);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800442 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443 func_exit();
444 return 1;
445 }
446
Jeff Garzikd61780c2005-10-30 15:01:51 -0800447 /* Check the DSR lines that Specialix uses as board
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448 identification */
Alan Coxa72492b2008-07-22 11:17:05 +0100449 val1 = read_cross_byte(bp, CD186x_MSVR, MSVR_DSR);
450 val2 = read_cross_byte(bp, CD186x_MSVR, MSVR_RTS);
451 dprintk(SX_DEBUG_INIT,
452 "sx%d: DSR lines are: %02x, rts lines are: %02x\n",
453 board_No(bp), val1, val2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454
455 /* They managed to switch the bit order between the docs and
456 the IO8+ card. The new PCI card now conforms to old docs.
457 They changed the PCI docs to reflect the situation on the
458 old card. */
459 val2 = (bp->flags & SX_BOARD_IS_PCI)?0x4d : 0xb2;
460 if (val1 != val2) {
Alan Coxa72492b2008-07-22 11:17:05 +0100461 printk(KERN_INFO
462 "sx%d: specialix IO8+ ID %02x at 0x%03x not found (%02x).\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463 board_No(bp), val2, bp->base, val1);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800464 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465 func_exit();
466 return 1;
467 }
468
469
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470 /* Reset CD186x again */
471 if (!sx_init_CD186x(bp)) {
Jeff Garzikd61780c2005-10-30 15:01:51 -0800472 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473 func_exit();
Jeff Garzikd61780c2005-10-30 15:01:51 -0800474 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475 }
476
477 sx_request_io_range(bp);
478 bp->flags |= SX_BOARD_PRESENT;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800479
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480 /* Chip revcode pkgtype
Alan Coxa72492b2008-07-22 11:17:05 +0100481 GFRCR SRCR bit 7
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 CD180 rev B 0x81 0
483 CD180 rev C 0x82 0
484 CD1864 rev A 0x82 1
Jeff Garzikd61780c2005-10-30 15:01:51 -0800485 CD1865 rev A 0x83 1 -- Do not use!!! Does not work.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486 CD1865 rev B 0x84 1
487 -- Thanks to Gwen Wang, Cirrus Logic.
488 */
489
490 switch (sx_in_off(bp, CD186x_GFRCR)) {
Alan Coxa72492b2008-07-22 11:17:05 +0100491 case 0x82:
492 chip = 1864;
493 rev = 'A';
494 break;
495 case 0x83:
496 chip = 1865;
497 rev = 'A';
498 break;
499 case 0x84:
500 chip = 1865;
501 rev = 'B';
502 break;
503 case 0x85:
504 chip = 1865;
505 rev = 'C';
506 break; /* Does not exist at this time */
507 default:
508 chip = -1;
509 rev = 'x';
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510 }
511
Alan Coxa72492b2008-07-22 11:17:05 +0100512 dprintk(SX_DEBUG_INIT, " GFCR = 0x%02x\n", sx_in_off(bp, CD186x_GFRCR));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513
Alan Coxa72492b2008-07-22 11:17:05 +0100514 printk(KERN_INFO
515 "sx%d: specialix IO8+ board detected at 0x%03x, IRQ %d, CD%d Rev. %c.\n",
516 board_No(bp), bp->base, bp->irq, chip, rev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517
518 func_exit();
519 return 0;
520}
521
Jeff Garzikd61780c2005-10-30 15:01:51 -0800522/*
523 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524 * Interrupt processing routines.
525 * */
526
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100527static struct specialix_port *sx_get_port(struct specialix_board *bp,
Alan Coxa72492b2008-07-22 11:17:05 +0100528 unsigned char const *what)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529{
530 unsigned char channel;
Alan Coxa72492b2008-07-22 11:17:05 +0100531 struct specialix_port *port = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532
533 channel = sx_in(bp, CD186x_GICR) >> GICR_CHAN_OFF;
Alan Coxa72492b2008-07-22 11:17:05 +0100534 dprintk(SX_DEBUG_CHAN, "channel: %d\n", channel);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 if (channel < CD186x_NCH) {
536 port = &sx_port[board_No(bp) * SX_NPORT + channel];
Alan Coxa72492b2008-07-22 11:17:05 +0100537 dprintk(SX_DEBUG_CHAN, "port: %d %p flags: 0x%lx\n",
538 board_No(bp) * SX_NPORT + channel, port,
539 port->port.flags & ASYNC_INITIALIZED);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540
Alan Cox44b7d1b2008-07-16 21:57:18 +0100541 if (port->port.flags & ASYNC_INITIALIZED) {
Alan Coxa72492b2008-07-22 11:17:05 +0100542 dprintk(SX_DEBUG_CHAN, "port: %d %p\n", channel, port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 func_exit();
544 return port;
545 }
546 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800547 printk(KERN_INFO "sx%d: %s interrupt from invalid port %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 board_No(bp), what, channel);
549 return NULL;
550}
551
552
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100553static void sx_receive_exc(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554{
555 struct specialix_port *port;
556 struct tty_struct *tty;
557 unsigned char status;
Alan Cox33f0f882006-01-09 20:54:13 -0800558 unsigned char ch, flag;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559
560 func_enter();
561
562 port = sx_get_port(bp, "Receive");
563 if (!port) {
Alan Coxa72492b2008-07-22 11:17:05 +0100564 dprintk(SX_DEBUG_RX, "Hmm, couldn't find port.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565 func_exit();
566 return;
567 }
Alan Cox44b7d1b2008-07-16 21:57:18 +0100568 tty = port->port.tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800569
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570 status = sx_in(bp, CD186x_RCSR);
571
Alan Coxa72492b2008-07-22 11:17:05 +0100572 dprintk(SX_DEBUG_RX, "status: 0x%x\n", status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573 if (status & RCSR_OE) {
574 port->overrun++;
Alan Coxa72492b2008-07-22 11:17:05 +0100575 dprintk(SX_DEBUG_FIFO,
576 "sx%d: port %d: Overrun. Total %ld overruns.\n",
577 board_No(bp), port_No(port), port->overrun);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 }
579 status &= port->mark_mask;
580
581 /* This flip buffer check needs to be below the reading of the
582 status register to reset the chip's IRQ.... */
Alan Cox33f0f882006-01-09 20:54:13 -0800583 if (tty_buffer_request_room(tty, 1) == 0) {
Alan Coxa72492b2008-07-22 11:17:05 +0100584 dprintk(SX_DEBUG_FIFO,
585 "sx%d: port %d: Working around flip buffer overflow.\n",
586 board_No(bp), port_No(port));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587 func_exit();
588 return;
589 }
590
591 ch = sx_in(bp, CD186x_RDR);
592 if (!status) {
593 func_exit();
594 return;
595 }
596 if (status & RCSR_TOUT) {
Alan Coxa72492b2008-07-22 11:17:05 +0100597 printk(KERN_INFO
598 "sx%d: port %d: Receiver timeout. Hardware problems ?\n",
599 board_No(bp), port_No(port));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600 func_exit();
601 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800602
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603 } else if (status & RCSR_BREAK) {
604 dprintk(SX_DEBUG_RX, "sx%d: port %d: Handling break...\n",
605 board_No(bp), port_No(port));
Alan Cox33f0f882006-01-09 20:54:13 -0800606 flag = TTY_BREAK;
Alan Cox44b7d1b2008-07-16 21:57:18 +0100607 if (port->port.flags & ASYNC_SAK)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608 do_SAK(tty);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800609
610 } else if (status & RCSR_PE)
Alan Cox33f0f882006-01-09 20:54:13 -0800611 flag = TTY_PARITY;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800612
613 else if (status & RCSR_FE)
Alan Cox33f0f882006-01-09 20:54:13 -0800614 flag = TTY_FRAME;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800615
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616 else if (status & RCSR_OE)
Alan Cox33f0f882006-01-09 20:54:13 -0800617 flag = TTY_OVERRUN;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800618
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619 else
Alan Cox33f0f882006-01-09 20:54:13 -0800620 flag = TTY_NORMAL;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800621
Alan Coxa72492b2008-07-22 11:17:05 +0100622 if (tty_insert_flip_char(tty, ch, flag))
Alan Cox33f0f882006-01-09 20:54:13 -0800623 tty_flip_buffer_push(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624 func_exit();
625}
626
627
Alan Coxd2fbd0f2008-07-22 11:17:16 +0100628static void sx_receive(struct specialix_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629{
630 struct specialix_port *port;
631 struct tty_struct *tty;
632 unsigned char count;
633
634 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -0800635
Alan Coxa72492b2008-07-22 11:17:05 +0100636 port = sx_get_port(bp, "Receive");
637 if (port == NULL) {
638 dprintk(SX_DEBUG_RX, "Hmm, couldn't find port.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639 func_exit();
640 return;
641 }
Alan Cox44b7d1b2008-07-16 21:57:18 +0100642 tty = port->port.tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800643
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644 count = sx_in(bp, CD186x_RDCR);
Alan Coxa72492b2008-07-22 11:17:05 +0100645 dprintk(SX_DEBUG_RX, "port: %p: count: %d\n", port, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646 port->hits[count > 8 ? 9 : count]++;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800647
Alan Cox33f0f882006-01-09 20:54:13 -0800648 tty_buffer_request_room(tty, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649
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 }
1368 schedule();
1369 }
1370
1371 set_current_state(TASK_RUNNING);
Alan Cox44b7d1b2008-07-16 21:57:18 +01001372 remove_wait_queue(&port->port.open_wait, &wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373 spin_lock_irqsave(&port->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001374 if (!tty_hung_up_p(filp))
Alan Cox44b7d1b2008-07-16 21:57:18 +01001375 port->port.count++;
Alan Cox44b7d1b2008-07-16 21:57:18 +01001376 port->port.blocked_open--;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377 spin_unlock_irqrestore(&port->lock, flags);
1378 if (retval) {
1379 func_exit();
1380 return retval;
1381 }
1382
Alan Cox44b7d1b2008-07-16 21:57:18 +01001383 port->port.flags |= ASYNC_NORMAL_ACTIVE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384 func_exit();
1385 return 0;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001386}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001387
1388
Alan Coxa72492b2008-07-22 11:17:05 +01001389static int sx_open(struct tty_struct *tty, struct file *filp)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001390{
1391 int board;
1392 int error;
Alan Coxa72492b2008-07-22 11:17:05 +01001393 struct specialix_port *port;
1394 struct specialix_board *bp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001395 int i;
1396 unsigned long flags;
1397
1398 func_enter();
1399
1400 board = SX_BOARD(tty->index);
1401
1402 if (board >= SX_NBOARD || !(sx_board[board].flags & SX_BOARD_PRESENT)) {
1403 func_exit();
1404 return -ENODEV;
1405 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001406
Linus Torvalds1da177e2005-04-16 15:20:36 -07001407 bp = &sx_board[board];
1408 port = sx_port + board * SX_NPORT + SX_PORT(tty->index);
1409 port->overrun = 0;
1410 for (i = 0; i < 10; i++)
Alan Coxa72492b2008-07-22 11:17:05 +01001411 port->hits[i] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001412
Alan Coxa72492b2008-07-22 11:17:05 +01001413 dprintk(SX_DEBUG_OPEN,
1414 "Board = %d, bp = %p, port = %p, portno = %d.\n",
1415 board, bp, port, SX_PORT(tty->index));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001416
1417 if (sx_paranoia_check(port, tty->name, "sx_open")) {
1418 func_enter();
1419 return -ENODEV;
1420 }
1421
Alan Coxa72492b2008-07-22 11:17:05 +01001422 error = sx_setup_board(bp);
1423 if (error) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001424 func_exit();
1425 return error;
1426 }
1427
1428 spin_lock_irqsave(&bp->lock, flags);
Alan Cox44b7d1b2008-07-16 21:57:18 +01001429 port->port.count++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001430 bp->count++;
1431 tty->driver_data = port;
Alan Cox44b7d1b2008-07-16 21:57:18 +01001432 port->port.tty = tty;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001433 spin_unlock_irqrestore(&bp->lock, flags);
1434
Alan Coxa72492b2008-07-22 11:17:05 +01001435 error = sx_setup_port(bp, port);
1436 if (error) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001437 func_enter();
1438 return error;
1439 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001440
Alan Coxa72492b2008-07-22 11:17:05 +01001441 error = block_til_ready(tty, filp, port);
1442 if (error) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001443 func_enter();
1444 return error;
1445 }
1446
1447 func_exit();
1448 return 0;
1449}
1450
Alan Cox978e5952008-04-30 00:53:59 -07001451static void sx_flush_buffer(struct tty_struct *tty)
1452{
Alan Coxc9f19e92009-01-02 13:47:26 +00001453 struct specialix_port *port = tty->driver_data;
Alan Cox978e5952008-04-30 00:53:59 -07001454 unsigned long flags;
Alan Coxa72492b2008-07-22 11:17:05 +01001455 struct specialix_board *bp;
Alan Cox978e5952008-04-30 00:53:59 -07001456
1457 func_enter();
1458
1459 if (sx_paranoia_check(port, tty->name, "sx_flush_buffer")) {
1460 func_exit();
1461 return;
1462 }
1463
1464 bp = port_Board(port);
1465 spin_lock_irqsave(&port->lock, flags);
1466 port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
1467 spin_unlock_irqrestore(&port->lock, flags);
1468 tty_wakeup(tty);
1469
1470 func_exit();
1471}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001472
Alan Coxa72492b2008-07-22 11:17:05 +01001473static void sx_close(struct tty_struct *tty, struct file *filp)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001474{
Alan Coxc9f19e92009-01-02 13:47:26 +00001475 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001476 struct specialix_board *bp;
1477 unsigned long flags;
1478 unsigned long timeout;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001479
Linus Torvalds1da177e2005-04-16 15:20:36 -07001480 func_enter();
1481 if (!port || sx_paranoia_check(port, tty->name, "close")) {
1482 func_exit();
1483 return;
1484 }
1485 spin_lock_irqsave(&port->lock, flags);
1486
1487 if (tty_hung_up_p(filp)) {
1488 spin_unlock_irqrestore(&port->lock, flags);
1489 func_exit();
1490 return;
1491 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001492
Linus Torvalds1da177e2005-04-16 15:20:36 -07001493 bp = port_Board(port);
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001494 if (tty->count == 1 && port->port.count != 1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001495 printk(KERN_ERR "sx%d: sx_close: bad port count;"
1496 " tty->count is 1, port count is %d\n",
Alan Cox44b7d1b2008-07-16 21:57:18 +01001497 board_No(bp), port->port.count);
1498 port->port.count = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499 }
1500
Alan Cox44b7d1b2008-07-16 21:57:18 +01001501 if (port->port.count > 1) {
1502 port->port.count--;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001503 bp->count--;
1504
1505 spin_unlock_irqrestore(&port->lock, flags);
1506
1507 func_exit();
1508 return;
1509 }
Alan Cox44b7d1b2008-07-16 21:57:18 +01001510 port->port.flags |= ASYNC_CLOSING;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001511 /*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001512 * Now we wait for the transmit buffer to clear; and we notify
Linus Torvalds1da177e2005-04-16 15:20:36 -07001513 * the line discipline to only process XON/XOFF characters.
1514 */
1515 tty->closing = 1;
1516 spin_unlock_irqrestore(&port->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001517 dprintk(SX_DEBUG_OPEN, "Closing\n");
1518 if (port->port.closing_wait != ASYNC_CLOSING_WAIT_NONE)
Alan Cox44b7d1b2008-07-16 21:57:18 +01001519 tty_wait_until_sent(tty, port->port.closing_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001520 /*
1521 * At this point we stop accepting input. To do this, we
1522 * disable the receive line status interrupts, and tell the
1523 * interrupt driver to stop checking the data ready bit in the
1524 * line status register.
1525 */
Alan Coxa72492b2008-07-22 11:17:05 +01001526 dprintk(SX_DEBUG_OPEN, "Closed\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527 port->IER &= ~IER_RXD;
Alan Cox44b7d1b2008-07-16 21:57:18 +01001528 if (port->port.flags & ASYNC_INITIALIZED) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529 port->IER &= ~IER_TXRDY;
1530 port->IER |= IER_TXEMPTY;
1531 spin_lock_irqsave(&bp->lock, flags);
1532 sx_out(bp, CD186x_CAR, port_No(port));
1533 sx_out(bp, CD186x_IER, port->IER);
1534 spin_unlock_irqrestore(&bp->lock, flags);
1535 /*
1536 * Before we drop DTR, make sure the UART transmitter
1537 * has completely drained; this is especially
1538 * important if there is a transmit FIFO!
1539 */
1540 timeout = jiffies+HZ;
Alan Coxa72492b2008-07-22 11:17:05 +01001541 while (port->IER & IER_TXEMPTY) {
1542 set_current_state(TASK_INTERRUPTIBLE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001543 msleep_interruptible(jiffies_to_msecs(port->timeout));
1544 if (time_after(jiffies, timeout)) {
Alan Coxa72492b2008-07-22 11:17:05 +01001545 printk(KERN_INFO "Timeout waiting for close\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001546 break;
1547 }
1548 }
1549
1550 }
1551
1552 if (--bp->count < 0) {
Alan Coxa72492b2008-07-22 11:17:05 +01001553 printk(KERN_ERR
1554 "sx%d: sx_shutdown_port: bad board count: %d port: %d\n",
1555 board_No(bp), bp->count, tty->index);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556 bp->count = 0;
1557 }
Alan Cox44b7d1b2008-07-16 21:57:18 +01001558 if (--port->port.count < 0) {
Alan Coxa72492b2008-07-22 11:17:05 +01001559 printk(KERN_ERR
1560 "sx%d: sx_close: bad port count for tty%d: %d\n",
1561 board_No(bp), port_No(port), port->port.count);
Alan Cox44b7d1b2008-07-16 21:57:18 +01001562 port->port.count = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563 }
1564
1565 sx_shutdown_port(bp, port);
Alan Cox978e5952008-04-30 00:53:59 -07001566 sx_flush_buffer(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001567 tty_ldisc_flush(tty);
1568 spin_lock_irqsave(&port->lock, flags);
1569 tty->closing = 0;
Alan Cox44b7d1b2008-07-16 21:57:18 +01001570 port->port.tty = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001571 spin_unlock_irqrestore(&port->lock, flags);
Alan Cox44b7d1b2008-07-16 21:57:18 +01001572 if (port->port.blocked_open) {
Alan Coxa72492b2008-07-22 11:17:05 +01001573 if (port->port.close_delay)
1574 msleep_interruptible(
1575 jiffies_to_msecs(port->port.close_delay));
Alan Cox44b7d1b2008-07-16 21:57:18 +01001576 wake_up_interruptible(&port->port.open_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001577 }
Alan Cox44b7d1b2008-07-16 21:57:18 +01001578 port->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
1579 wake_up_interruptible(&port->port.close_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001580
1581 func_exit();
1582}
1583
1584
Alan Coxa72492b2008-07-22 11:17:05 +01001585static int sx_write(struct tty_struct *tty,
1586 const unsigned char *buf, int count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001587{
Alan Coxc9f19e92009-01-02 13:47:26 +00001588 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001589 struct specialix_board *bp;
1590 int c, total = 0;
1591 unsigned long flags;
1592
1593 func_enter();
1594 if (sx_paranoia_check(port, tty->name, "sx_write")) {
1595 func_exit();
1596 return 0;
1597 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001598
Linus Torvalds1da177e2005-04-16 15:20:36 -07001599 bp = port_Board(port);
1600
Jiri Slaby365e0222006-09-30 23:28:11 -07001601 if (!port->xmit_buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001602 func_exit();
1603 return 0;
1604 }
1605
1606 while (1) {
1607 spin_lock_irqsave(&port->lock, flags);
1608 c = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
1609 SERIAL_XMIT_SIZE - port->xmit_head));
1610 if (c <= 0) {
1611 spin_unlock_irqrestore(&port->lock, flags);
1612 break;
1613 }
1614 memcpy(port->xmit_buf + port->xmit_head, buf, c);
1615 port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
1616 port->xmit_cnt += c;
1617 spin_unlock_irqrestore(&port->lock, flags);
1618
1619 buf += c;
1620 count -= c;
1621 total += c;
1622 }
1623
1624 spin_lock_irqsave(&bp->lock, flags);
1625 if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
1626 !(port->IER & IER_TXRDY)) {
1627 port->IER |= IER_TXRDY;
1628 sx_out(bp, CD186x_CAR, port_No(port));
1629 sx_out(bp, CD186x_IER, port->IER);
1630 }
1631 spin_unlock_irqrestore(&bp->lock, flags);
1632 func_exit();
1633
1634 return total;
1635}
1636
1637
Alan Coxa72492b2008-07-22 11:17:05 +01001638static int sx_put_char(struct tty_struct *tty, unsigned char ch)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001639{
Alan Coxc9f19e92009-01-02 13:47:26 +00001640 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641 unsigned long flags;
Alan Coxa72492b2008-07-22 11:17:05 +01001642 struct specialix_board *bp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001643
1644 func_enter();
1645
1646 if (sx_paranoia_check(port, tty->name, "sx_put_char")) {
1647 func_exit();
Alan Cox6ae04572008-04-30 00:54:07 -07001648 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001649 }
Alan Coxa72492b2008-07-22 11:17:05 +01001650 dprintk(SX_DEBUG_TX, "check tty: %p %p\n", tty, port->xmit_buf);
Eric Sesterhenn326f28e92006-06-25 05:48:48 -07001651 if (!port->xmit_buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001652 func_exit();
Alan Cox6ae04572008-04-30 00:54:07 -07001653 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001654 }
1655 bp = port_Board(port);
1656 spin_lock_irqsave(&port->lock, flags);
1657
Alan Coxa72492b2008-07-22 11:17:05 +01001658 dprintk(SX_DEBUG_TX, "xmit_cnt: %d xmit_buf: %p\n",
1659 port->xmit_cnt, port->xmit_buf);
1660 if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1 || !port->xmit_buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001661 spin_unlock_irqrestore(&port->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001662 dprintk(SX_DEBUG_TX, "Exit size\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663 func_exit();
Alan Cox6ae04572008-04-30 00:54:07 -07001664 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001665 }
Alan Coxa72492b2008-07-22 11:17:05 +01001666 dprintk(SX_DEBUG_TX, "Handle xmit: %p %p\n", port, port->xmit_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667 port->xmit_buf[port->xmit_head++] = ch;
1668 port->xmit_head &= SERIAL_XMIT_SIZE - 1;
1669 port->xmit_cnt++;
1670 spin_unlock_irqrestore(&port->lock, flags);
1671
1672 func_exit();
Alan Cox6ae04572008-04-30 00:54:07 -07001673 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001674}
1675
1676
Alan Coxa72492b2008-07-22 11:17:05 +01001677static void sx_flush_chars(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001678{
Alan Coxc9f19e92009-01-02 13:47:26 +00001679 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001680 unsigned long flags;
Alan Coxa72492b2008-07-22 11:17:05 +01001681 struct specialix_board *bp = port_Board(port);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001682
1683 func_enter();
1684
1685 if (sx_paranoia_check(port, tty->name, "sx_flush_chars")) {
1686 func_exit();
1687 return;
1688 }
1689 if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
1690 !port->xmit_buf) {
1691 func_exit();
1692 return;
1693 }
1694 spin_lock_irqsave(&bp->lock, flags);
1695 port->IER |= IER_TXRDY;
1696 sx_out(port_Board(port), CD186x_CAR, port_No(port));
1697 sx_out(port_Board(port), CD186x_IER, port->IER);
1698 spin_unlock_irqrestore(&bp->lock, flags);
1699
1700 func_exit();
1701}
1702
1703
Alan Coxa72492b2008-07-22 11:17:05 +01001704static int sx_write_room(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001705{
Alan Coxc9f19e92009-01-02 13:47:26 +00001706 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001707 int ret;
1708
1709 func_enter();
1710
1711 if (sx_paranoia_check(port, tty->name, "sx_write_room")) {
1712 func_exit();
1713 return 0;
1714 }
1715
1716 ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;
1717 if (ret < 0)
1718 ret = 0;
1719
1720 func_exit();
1721 return ret;
1722}
1723
1724
1725static int sx_chars_in_buffer(struct tty_struct *tty)
1726{
Alan Coxc9f19e92009-01-02 13:47:26 +00001727 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001728
1729 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08001730
Linus Torvalds1da177e2005-04-16 15:20:36 -07001731 if (sx_paranoia_check(port, tty->name, "sx_chars_in_buffer")) {
1732 func_exit();
1733 return 0;
1734 }
1735 func_exit();
1736 return port->xmit_cnt;
1737}
1738
Linus Torvalds1da177e2005-04-16 15:20:36 -07001739static int sx_tiocmget(struct tty_struct *tty, struct file *file)
1740{
Alan Coxc9f19e92009-01-02 13:47:26 +00001741 struct specialix_port *port = tty->driver_data;
Alan Coxa72492b2008-07-22 11:17:05 +01001742 struct specialix_board *bp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001743 unsigned char status;
1744 unsigned int result;
1745 unsigned long flags;
1746
1747 func_enter();
1748
Harvey Harrisonbf9d8922008-04-30 00:55:10 -07001749 if (sx_paranoia_check(port, tty->name, __func__)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001750 func_exit();
1751 return -ENODEV;
1752 }
1753
1754 bp = port_Board(port);
Alan Coxa72492b2008-07-22 11:17:05 +01001755 spin_lock_irqsave(&bp->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001756 sx_out(bp, CD186x_CAR, port_No(port));
1757 status = sx_in(bp, CD186x_MSVR);
1758 spin_unlock_irqrestore(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001759 dprintk(SX_DEBUG_INIT, "Got msvr[%d] = %02x, car = %d.\n",
1760 port_No(port), status, sx_in(bp, CD186x_CAR));
1761 dprintk(SX_DEBUG_INIT, "sx_port = %p, port = %p\n", sx_port, port);
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001762 if (sx_crtscts(port->port.tty)) {
1763 result = TIOCM_DTR | TIOCM_DSR
Alan Coxa72492b2008-07-22 11:17:05 +01001764 | ((status & MSVR_DTR) ? TIOCM_RTS : 0)
1765 | ((status & MSVR_CD) ? TIOCM_CAR : 0)
Alan Coxa72492b2008-07-22 11:17:05 +01001766 | ((status & MSVR_CTS) ? TIOCM_CTS : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001767 } else {
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001768 result = TIOCM_RTS | TIOCM_DSR
Alan Coxa72492b2008-07-22 11:17:05 +01001769 | ((status & MSVR_DTR) ? TIOCM_DTR : 0)
1770 | ((status & MSVR_CD) ? TIOCM_CAR : 0)
Alan Coxa72492b2008-07-22 11:17:05 +01001771 | ((status & MSVR_CTS) ? TIOCM_CTS : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001772 }
1773
1774 func_exit();
1775
1776 return result;
1777}
1778
1779
1780static int sx_tiocmset(struct tty_struct *tty, struct file *file,
1781 unsigned int set, unsigned int clear)
1782{
Alan Coxc9f19e92009-01-02 13:47:26 +00001783 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001784 unsigned long flags;
1785 struct specialix_board *bp;
1786
1787 func_enter();
1788
Harvey Harrisonbf9d8922008-04-30 00:55:10 -07001789 if (sx_paranoia_check(port, tty->name, __func__)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001790 func_exit();
1791 return -ENODEV;
1792 }
1793
1794 bp = port_Board(port);
1795
1796 spin_lock_irqsave(&port->lock, flags);
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001797 if (sx_crtscts(port->port.tty)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001798 if (set & TIOCM_RTS)
1799 port->MSVR |= MSVR_DTR;
1800 } else {
1801 if (set & TIOCM_DTR)
1802 port->MSVR |= MSVR_DTR;
1803 }
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001804 if (sx_crtscts(port->port.tty)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001805 if (clear & TIOCM_RTS)
1806 port->MSVR &= ~MSVR_DTR;
1807 } else {
1808 if (clear & TIOCM_DTR)
1809 port->MSVR &= ~MSVR_DTR;
1810 }
1811 spin_lock_irqsave(&bp->lock, flags);
1812 sx_out(bp, CD186x_CAR, port_No(port));
1813 sx_out(bp, CD186x_MSVR, port->MSVR);
1814 spin_unlock_irqrestore(&bp->lock, flags);
1815 spin_unlock_irqrestore(&port->lock, flags);
1816 func_exit();
1817 return 0;
1818}
1819
1820
Alan Coxfaa76122008-07-22 11:19:05 +01001821static int sx_send_break(struct tty_struct *tty, int length)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001822{
Alan Coxc9f19e92009-01-02 13:47:26 +00001823 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001824 struct specialix_board *bp = port_Board(port);
1825 unsigned long flags;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001826
Linus Torvalds1da177e2005-04-16 15:20:36 -07001827 func_enter();
Alan Coxfaa76122008-07-22 11:19:05 +01001828 if (length == 0 || length == -1)
1829 return -EOPNOTSUPP;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001830
Alan Coxa72492b2008-07-22 11:17:05 +01001831 spin_lock_irqsave(&port->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001832 port->break_length = SPECIALIX_TPS / HZ * length;
1833 port->COR2 |= COR2_ETC;
1834 port->IER |= IER_TXRDY;
1835 spin_lock_irqsave(&bp->lock, flags);
1836 sx_out(bp, CD186x_CAR, port_No(port));
1837 sx_out(bp, CD186x_COR2, port->COR2);
1838 sx_out(bp, CD186x_IER, port->IER);
1839 spin_unlock_irqrestore(&bp->lock, flags);
Alan Coxa72492b2008-07-22 11:17:05 +01001840 spin_unlock_irqrestore(&port->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001841 sx_wait_CCR(bp);
1842 spin_lock_irqsave(&bp->lock, flags);
1843 sx_out(bp, CD186x_CCR, CCR_CORCHG2);
1844 spin_unlock_irqrestore(&bp->lock, flags);
1845 sx_wait_CCR(bp);
1846
1847 func_exit();
Alan Coxfaa76122008-07-22 11:19:05 +01001848 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001849}
1850
1851
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001852static int sx_set_serial_info(struct specialix_port *port,
Alan Coxa72492b2008-07-22 11:17:05 +01001853 struct serial_struct __user *newinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001854{
1855 struct serial_struct tmp;
1856 struct specialix_board *bp = port_Board(port);
1857 int change_speed;
1858
1859 func_enter();
Alan Coxb190e172008-04-30 00:53:22 -07001860
Linus Torvalds1da177e2005-04-16 15:20:36 -07001861 if (copy_from_user(&tmp, newinfo, sizeof(tmp))) {
1862 func_enter();
1863 return -EFAULT;
1864 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001865
Alan Coxb190e172008-04-30 00:53:22 -07001866 lock_kernel();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001867
Alan Cox44b7d1b2008-07-16 21:57:18 +01001868 change_speed = ((port->port.flags & ASYNC_SPD_MASK) !=
Linus Torvalds1da177e2005-04-16 15:20:36 -07001869 (tmp.flags & ASYNC_SPD_MASK));
1870 change_speed |= (tmp.custom_divisor != port->custom_divisor);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001871
Linus Torvalds1da177e2005-04-16 15:20:36 -07001872 if (!capable(CAP_SYS_ADMIN)) {
Alan Cox44b7d1b2008-07-16 21:57:18 +01001873 if ((tmp.close_delay != port->port.close_delay) ||
1874 (tmp.closing_wait != port->port.closing_wait) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001875 ((tmp.flags & ~ASYNC_USR_MASK) !=
Alan Cox44b7d1b2008-07-16 21:57:18 +01001876 (port->port.flags & ~ASYNC_USR_MASK))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001877 func_exit();
Alan Coxb190e172008-04-30 00:53:22 -07001878 unlock_kernel();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001879 return -EPERM;
1880 }
Alan Cox44b7d1b2008-07-16 21:57:18 +01001881 port->port.flags = ((port->port.flags & ~ASYNC_USR_MASK) |
Alan Coxa72492b2008-07-22 11:17:05 +01001882 (tmp.flags & ASYNC_USR_MASK));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001883 port->custom_divisor = tmp.custom_divisor;
1884 } else {
Alan Cox44b7d1b2008-07-16 21:57:18 +01001885 port->port.flags = ((port->port.flags & ~ASYNC_FLAGS) |
Alan Coxa72492b2008-07-22 11:17:05 +01001886 (tmp.flags & ASYNC_FLAGS));
Alan Cox44b7d1b2008-07-16 21:57:18 +01001887 port->port.close_delay = tmp.close_delay;
1888 port->port.closing_wait = tmp.closing_wait;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001889 port->custom_divisor = tmp.custom_divisor;
1890 }
Alan Coxa72492b2008-07-22 11:17:05 +01001891 if (change_speed)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001892 sx_change_speed(bp, port);
Alan Coxa72492b2008-07-22 11:17:05 +01001893
Linus Torvalds1da177e2005-04-16 15:20:36 -07001894 func_exit();
Alan Coxb190e172008-04-30 00:53:22 -07001895 unlock_kernel();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001896 return 0;
1897}
1898
1899
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001900static int sx_get_serial_info(struct specialix_port *port,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001901 struct serial_struct __user *retinfo)
1902{
1903 struct serial_struct tmp;
1904 struct specialix_board *bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001905
Linus Torvalds1da177e2005-04-16 15:20:36 -07001906 func_enter();
1907
Linus Torvalds1da177e2005-04-16 15:20:36 -07001908 memset(&tmp, 0, sizeof(tmp));
Alan Coxb190e172008-04-30 00:53:22 -07001909 lock_kernel();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001910 tmp.type = PORT_CIRRUS;
1911 tmp.line = port - sx_port;
1912 tmp.port = bp->base;
1913 tmp.irq = bp->irq;
Alan Cox44b7d1b2008-07-16 21:57:18 +01001914 tmp.flags = port->port.flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001915 tmp.baud_base = (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC;
Alan Cox44b7d1b2008-07-16 21:57:18 +01001916 tmp.close_delay = port->port.close_delay * HZ/100;
1917 tmp.closing_wait = port->port.closing_wait * HZ/100;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001918 tmp.custom_divisor = port->custom_divisor;
1919 tmp.xmit_fifo_size = CD186x_NFIFO;
Alan Coxb190e172008-04-30 00:53:22 -07001920 unlock_kernel();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001921 if (copy_to_user(retinfo, &tmp, sizeof(tmp))) {
1922 func_exit();
1923 return -EFAULT;
1924 }
1925
1926 func_exit();
1927 return 0;
1928}
1929
1930
Alan Coxa72492b2008-07-22 11:17:05 +01001931static int sx_ioctl(struct tty_struct *tty, struct file *filp,
1932 unsigned int cmd, unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001933{
Alan Coxc9f19e92009-01-02 13:47:26 +00001934 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001935 void __user *argp = (void __user *)arg;
1936
1937 func_enter();
1938
1939 if (sx_paranoia_check(port, tty->name, "sx_ioctl")) {
1940 func_exit();
1941 return -ENODEV;
1942 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001943
Linus Torvalds1da177e2005-04-16 15:20:36 -07001944 switch (cmd) {
Alan Coxa72492b2008-07-22 11:17:05 +01001945 case TIOCGSERIAL:
Alan Coxfaa76122008-07-22 11:19:05 +01001946 func_exit();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001947 return sx_get_serial_info(port, argp);
Alan Coxa72492b2008-07-22 11:17:05 +01001948 case TIOCSSERIAL:
Alan Coxfaa76122008-07-22 11:19:05 +01001949 func_exit();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001950 return sx_set_serial_info(port, argp);
Alan Coxa72492b2008-07-22 11:17:05 +01001951 default:
Alan Coxfaa76122008-07-22 11:19:05 +01001952 func_exit();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001953 return -ENOIOCTLCMD;
1954 }
1955 func_exit();
1956 return 0;
1957}
1958
1959
Alan Coxa72492b2008-07-22 11:17:05 +01001960static void sx_throttle(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001961{
Alan Coxc9f19e92009-01-02 13:47:26 +00001962 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001963 struct specialix_board *bp;
1964 unsigned long flags;
1965
1966 func_enter();
1967
1968 if (sx_paranoia_check(port, tty->name, "sx_throttle")) {
1969 func_exit();
1970 return;
1971 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001972
Linus Torvalds1da177e2005-04-16 15:20:36 -07001973 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001974
Linus Torvalds1da177e2005-04-16 15:20:36 -07001975 /* Use DTR instead of RTS ! */
Alan Coxd2fbd0f2008-07-22 11:17:16 +01001976 if (sx_crtscts(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001977 port->MSVR &= ~MSVR_DTR;
1978 else {
1979 /* Auch!!! I think the system shouldn't call this then. */
1980 /* Or maybe we're supposed (allowed?) to do our side of hw
Jeff Garzikd61780c2005-10-30 15:01:51 -08001981 handshake anyway, even when hardware handshake is off.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001982 When you see this in your logs, please report.... */
Alan Coxa72492b2008-07-22 11:17:05 +01001983 printk(KERN_ERR
1984 "sx%d: Need to throttle, but can't (hardware hs is off)\n",
1985 port_No(port));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001986 }
1987 spin_lock_irqsave(&bp->lock, flags);
1988 sx_out(bp, CD186x_CAR, port_No(port));
1989 spin_unlock_irqrestore(&bp->lock, flags);
1990 if (I_IXOFF(tty)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001991 sx_wait_CCR(bp);
1992 spin_lock_irqsave(&bp->lock, flags);
1993 sx_out(bp, CD186x_CCR, CCR_SSCH2);
1994 spin_unlock_irqrestore(&bp->lock, flags);
1995 sx_wait_CCR(bp);
1996 }
1997 spin_lock_irqsave(&bp->lock, flags);
1998 sx_out(bp, CD186x_MSVR, port->MSVR);
1999 spin_unlock_irqrestore(&bp->lock, flags);
2000
2001 func_exit();
2002}
2003
2004
Alan Coxa72492b2008-07-22 11:17:05 +01002005static void sx_unthrottle(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002006{
Alan Coxc9f19e92009-01-02 13:47:26 +00002007 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002008 struct specialix_board *bp;
2009 unsigned long flags;
2010
2011 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002012
Linus Torvalds1da177e2005-04-16 15:20:36 -07002013 if (sx_paranoia_check(port, tty->name, "sx_unthrottle")) {
2014 func_exit();
2015 return;
2016 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002017
Linus Torvalds1da177e2005-04-16 15:20:36 -07002018 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002019
Linus Torvalds1da177e2005-04-16 15:20:36 -07002020 spin_lock_irqsave(&port->lock, flags);
2021 /* XXXX Use DTR INSTEAD???? */
Alan Coxd2fbd0f2008-07-22 11:17:16 +01002022 if (sx_crtscts(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002023 port->MSVR |= MSVR_DTR;
Alan Coxa72492b2008-07-22 11:17:05 +01002024 /* Else clause: see remark in "sx_throttle"... */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002025 spin_lock_irqsave(&bp->lock, flags);
2026 sx_out(bp, CD186x_CAR, port_No(port));
2027 spin_unlock_irqrestore(&bp->lock, flags);
2028 if (I_IXOFF(tty)) {
2029 spin_unlock_irqrestore(&port->lock, flags);
2030 sx_wait_CCR(bp);
2031 spin_lock_irqsave(&bp->lock, flags);
2032 sx_out(bp, CD186x_CCR, CCR_SSCH1);
2033 spin_unlock_irqrestore(&bp->lock, flags);
2034 sx_wait_CCR(bp);
2035 spin_lock_irqsave(&port->lock, flags);
2036 }
2037 spin_lock_irqsave(&bp->lock, flags);
2038 sx_out(bp, CD186x_MSVR, port->MSVR);
2039 spin_unlock_irqrestore(&bp->lock, flags);
2040 spin_unlock_irqrestore(&port->lock, flags);
2041
2042 func_exit();
2043}
2044
2045
Alan Coxa72492b2008-07-22 11:17:05 +01002046static void sx_stop(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002047{
Alan Coxc9f19e92009-01-02 13:47:26 +00002048 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002049 struct specialix_board *bp;
2050 unsigned long flags;
2051
2052 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002053
Linus Torvalds1da177e2005-04-16 15:20:36 -07002054 if (sx_paranoia_check(port, tty->name, "sx_stop")) {
2055 func_exit();
2056 return;
2057 }
2058
2059 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002060
Linus Torvalds1da177e2005-04-16 15:20:36 -07002061 spin_lock_irqsave(&port->lock, flags);
2062 port->IER &= ~IER_TXRDY;
2063 spin_lock_irqsave(&bp->lock, flags);
2064 sx_out(bp, CD186x_CAR, port_No(port));
2065 sx_out(bp, CD186x_IER, port->IER);
2066 spin_unlock_irqrestore(&bp->lock, flags);
2067 spin_unlock_irqrestore(&port->lock, flags);
2068
2069 func_exit();
2070}
2071
2072
Alan Coxa72492b2008-07-22 11:17:05 +01002073static void sx_start(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002074{
Alan Coxc9f19e92009-01-02 13:47:26 +00002075 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002076 struct specialix_board *bp;
2077 unsigned long flags;
2078
2079 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002080
Linus Torvalds1da177e2005-04-16 15:20:36 -07002081 if (sx_paranoia_check(port, tty->name, "sx_start")) {
2082 func_exit();
2083 return;
2084 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002085
Linus Torvalds1da177e2005-04-16 15:20:36 -07002086 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002087
Linus Torvalds1da177e2005-04-16 15:20:36 -07002088 spin_lock_irqsave(&port->lock, flags);
2089 if (port->xmit_cnt && port->xmit_buf && !(port->IER & IER_TXRDY)) {
2090 port->IER |= IER_TXRDY;
2091 spin_lock_irqsave(&bp->lock, flags);
2092 sx_out(bp, CD186x_CAR, port_No(port));
2093 sx_out(bp, CD186x_IER, port->IER);
2094 spin_unlock_irqrestore(&bp->lock, flags);
2095 }
2096 spin_unlock_irqrestore(&port->lock, flags);
2097
2098 func_exit();
2099}
2100
Alan Coxa72492b2008-07-22 11:17:05 +01002101static void sx_hangup(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002102{
Alan Coxc9f19e92009-01-02 13:47:26 +00002103 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002104 struct specialix_board *bp;
2105 unsigned long flags;
2106
2107 func_enter();
2108
2109 if (sx_paranoia_check(port, tty->name, "sx_hangup")) {
2110 func_exit();
2111 return;
2112 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002113
Linus Torvalds1da177e2005-04-16 15:20:36 -07002114 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002115
Linus Torvalds1da177e2005-04-16 15:20:36 -07002116 sx_shutdown_port(bp, port);
2117 spin_lock_irqsave(&port->lock, flags);
Alan Cox44b7d1b2008-07-16 21:57:18 +01002118 bp->count -= port->port.count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002119 if (bp->count < 0) {
Alan Coxa72492b2008-07-22 11:17:05 +01002120 printk(KERN_ERR
2121 "sx%d: sx_hangup: bad board count: %d port: %d\n",
2122 board_No(bp), bp->count, tty->index);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002123 bp->count = 0;
2124 }
Alan Cox44b7d1b2008-07-16 21:57:18 +01002125 port->port.count = 0;
2126 port->port.flags &= ~ASYNC_NORMAL_ACTIVE;
2127 port->port.tty = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002128 spin_unlock_irqrestore(&port->lock, flags);
Alan Cox44b7d1b2008-07-16 21:57:18 +01002129 wake_up_interruptible(&port->port.open_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002130
2131 func_exit();
2132}
2133
2134
Alan Coxa72492b2008-07-22 11:17:05 +01002135static void sx_set_termios(struct tty_struct *tty,
2136 struct ktermios *old_termios)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002137{
Alan Coxc9f19e92009-01-02 13:47:26 +00002138 struct specialix_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002139 unsigned long flags;
Alan Coxa72492b2008-07-22 11:17:05 +01002140 struct specialix_board *bp;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002141
Linus Torvalds1da177e2005-04-16 15:20:36 -07002142 if (sx_paranoia_check(port, tty->name, "sx_set_termios"))
2143 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002144
Linus Torvalds1da177e2005-04-16 15:20:36 -07002145 bp = port_Board(port);
2146 spin_lock_irqsave(&port->lock, flags);
2147 sx_change_speed(port_Board(port), port);
2148 spin_unlock_irqrestore(&port->lock, flags);
2149
2150 if ((old_termios->c_cflag & CRTSCTS) &&
2151 !(tty->termios->c_cflag & CRTSCTS)) {
2152 tty->hw_stopped = 0;
2153 sx_start(tty);
2154 }
2155}
2156
Jeff Dikeb68e31d2006-10-02 02:17:18 -07002157static const struct tty_operations sx_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002158 .open = sx_open,
2159 .close = sx_close,
2160 .write = sx_write,
2161 .put_char = sx_put_char,
2162 .flush_chars = sx_flush_chars,
2163 .write_room = sx_write_room,
2164 .chars_in_buffer = sx_chars_in_buffer,
2165 .flush_buffer = sx_flush_buffer,
2166 .ioctl = sx_ioctl,
2167 .throttle = sx_throttle,
2168 .unthrottle = sx_unthrottle,
2169 .set_termios = sx_set_termios,
2170 .stop = sx_stop,
2171 .start = sx_start,
2172 .hangup = sx_hangup,
2173 .tiocmget = sx_tiocmget,
2174 .tiocmset = sx_tiocmset,
Alan Coxfaa76122008-07-22 11:19:05 +01002175 .break_ctl = sx_send_break,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002176};
2177
2178static int sx_init_drivers(void)
2179{
2180 int error;
2181 int i;
2182
2183 func_enter();
2184
2185 specialix_driver = alloc_tty_driver(SX_NBOARD * SX_NPORT);
2186 if (!specialix_driver) {
2187 printk(KERN_ERR "sx: Couldn't allocate tty_driver.\n");
2188 func_exit();
2189 return 1;
2190 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002191
Linus Torvalds1da177e2005-04-16 15:20:36 -07002192 specialix_driver->owner = THIS_MODULE;
2193 specialix_driver->name = "ttyW";
2194 specialix_driver->major = SPECIALIX_NORMAL_MAJOR;
2195 specialix_driver->type = TTY_DRIVER_TYPE_SERIAL;
2196 specialix_driver->subtype = SERIAL_TYPE_NORMAL;
2197 specialix_driver->init_termios = tty_std_termios;
2198 specialix_driver->init_termios.c_cflag =
2199 B9600 | CS8 | CREAD | HUPCL | CLOCAL;
Alan Cox606d0992006-12-08 02:38:45 -08002200 specialix_driver->init_termios.c_ispeed = 9600;
2201 specialix_driver->init_termios.c_ospeed = 9600;
Alan Coxfaa76122008-07-22 11:19:05 +01002202 specialix_driver->flags = TTY_DRIVER_REAL_RAW |
2203 TTY_DRIVER_HARDWARE_BREAK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002204 tty_set_operations(specialix_driver, &sx_ops);
2205
Alan Coxa72492b2008-07-22 11:17:05 +01002206 error = tty_register_driver(specialix_driver);
2207 if (error) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002208 put_tty_driver(specialix_driver);
Alan Coxa72492b2008-07-22 11:17:05 +01002209 printk(KERN_ERR
2210 "sx: Couldn't register specialix IO8+ driver, error = %d\n",
2211 error);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002212 func_exit();
2213 return 1;
2214 }
2215 memset(sx_port, 0, sizeof(sx_port));
2216 for (i = 0; i < SX_NPORT * SX_NBOARD; i++) {
2217 sx_port[i].magic = SPECIALIX_MAGIC;
Alan Cox44b7d1b2008-07-16 21:57:18 +01002218 tty_port_init(&sx_port[i].port);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002219 spin_lock_init(&sx_port[i].lock);
2220 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002221
Linus Torvalds1da177e2005-04-16 15:20:36 -07002222 func_exit();
2223 return 0;
2224}
2225
2226static void sx_release_drivers(void)
2227{
2228 func_enter();
2229
Linus Torvalds1da177e2005-04-16 15:20:36 -07002230 tty_unregister_driver(specialix_driver);
2231 put_tty_driver(specialix_driver);
2232 func_exit();
2233}
2234
Jeff Garzikd61780c2005-10-30 15:01:51 -08002235/*
2236 * This routine must be called by kernel at boot time
Linus Torvalds1da177e2005-04-16 15:20:36 -07002237 */
2238static int __init specialix_init(void)
2239{
2240 int i;
2241 int found = 0;
2242
2243 func_enter();
2244
2245 printk(KERN_INFO "sx: Specialix IO8+ driver v" VERSION ", (c) R.E.Wolff 1997/1998.\n");
2246 printk(KERN_INFO "sx: derived from work (c) D.Gorodchanin 1994-1996.\n");
Alan Coxd2fbd0f2008-07-22 11:17:16 +01002247 if (sx_rtscts)
2248 printk(KERN_INFO
2249 "sx: DTR/RTS pin is RTS when CRTSCTS is on.\n");
2250 else
2251 printk(KERN_INFO "sx: DTR/RTS pin is always RTS.\n");
Jeff Garzikd61780c2005-10-30 15:01:51 -08002252
Linus Torvalds1da177e2005-04-16 15:20:36 -07002253 for (i = 0; i < SX_NBOARD; i++)
Ingo Molnar34af9462006-06-27 02:53:55 -07002254 spin_lock_init(&sx_board[i].lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002255
2256 if (sx_init_drivers()) {
2257 func_exit();
2258 return -EIO;
2259 }
2260
Jeff Garzikd61780c2005-10-30 15:01:51 -08002261 for (i = 0; i < SX_NBOARD; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002262 if (sx_board[i].base && !sx_probe(&sx_board[i]))
2263 found++;
2264
2265#ifdef CONFIG_PCI
2266 {
2267 struct pci_dev *pdev = NULL;
2268
Alan Coxa72492b2008-07-22 11:17:05 +01002269 i = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002270 while (i < SX_NBOARD) {
2271 if (sx_board[i].flags & SX_BOARD_PRESENT) {
2272 i++;
2273 continue;
2274 }
Alan Coxa72492b2008-07-22 11:17:05 +01002275 pdev = pci_get_device(PCI_VENDOR_ID_SPECIALIX,
2276 PCI_DEVICE_ID_SPECIALIX_IO8, pdev);
2277 if (!pdev)
2278 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002279
2280 if (pci_enable_device(pdev))
2281 continue;
2282
2283 sx_board[i].irq = pdev->irq;
2284
Alan Coxa72492b2008-07-22 11:17:05 +01002285 sx_board[i].base = pci_resource_start(pdev, 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002286
2287 sx_board[i].flags |= SX_BOARD_IS_PCI;
2288 if (!sx_probe(&sx_board[i]))
Alan Coxa72492b2008-07-22 11:17:05 +01002289 found++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002290 }
Alan Cox606d0992006-12-08 02:38:45 -08002291 /* May exit pci_get sequence early with lots of boards */
2292 if (pdev != NULL)
2293 pci_dev_put(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002294 }
2295#endif
2296
2297 if (!found) {
2298 sx_release_drivers();
2299 printk(KERN_INFO "sx: No specialix IO8+ boards detected.\n");
2300 func_exit();
2301 return -EIO;
2302 }
2303
2304 func_exit();
2305 return 0;
2306}
2307
2308static int iobase[SX_NBOARD] = {0,};
Alan Coxd2fbd0f2008-07-22 11:17:16 +01002309static int irq[SX_NBOARD] = {0,};
Linus Torvalds1da177e2005-04-16 15:20:36 -07002310
2311module_param_array(iobase, int, NULL, 0);
2312module_param_array(irq, int, NULL, 0);
2313module_param(sx_debug, int, 0);
Alan Coxd2fbd0f2008-07-22 11:17:16 +01002314module_param(sx_rtscts, int, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002315module_param(sx_rxfifo, int, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002316
2317/*
2318 * You can setup up to 4 boards.
2319 * by specifying "iobase=0xXXX,0xXXX ..." as insmod parameter.
Jeff Garzikd61780c2005-10-30 15:01:51 -08002320 * You should specify the IRQs too in that case "irq=....,...".
2321 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07002322 * More than 4 boards in one computer is not possible, as the card can
Jeff Garzikd61780c2005-10-30 15:01:51 -08002323 * only use 4 different interrupts.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002324 *
2325 */
2326static int __init specialix_init_module(void)
2327{
2328 int i;
2329
2330 func_enter();
2331
Linus Torvalds1da177e2005-04-16 15:20:36 -07002332 if (iobase[0] || iobase[1] || iobase[2] || iobase[3]) {
Alan Coxa72492b2008-07-22 11:17:05 +01002333 for (i = 0; i < SX_NBOARD; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002334 sx_board[i].base = iobase[i];
2335 sx_board[i].irq = irq[i];
Alan Coxa72492b2008-07-22 11:17:05 +01002336 sx_board[i].count = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002337 }
2338 }
2339
2340 func_exit();
2341
2342 return specialix_init();
2343}
Jeff Garzikd61780c2005-10-30 15:01:51 -08002344
Linus Torvalds1da177e2005-04-16 15:20:36 -07002345static void __exit specialix_exit_module(void)
2346{
2347 int i;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002348
Linus Torvalds1da177e2005-04-16 15:20:36 -07002349 func_enter();
2350
2351 sx_release_drivers();
2352 for (i = 0; i < SX_NBOARD; i++)
Jeff Garzikd61780c2005-10-30 15:01:51 -08002353 if (sx_board[i].flags & SX_BOARD_PRESENT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002354 sx_release_io_range(&sx_board[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002355 func_exit();
2356}
2357
Chuck Short7691030b2006-07-10 04:43:59 -07002358static struct pci_device_id specialx_pci_tbl[] __devinitdata = {
2359 { PCI_DEVICE(PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_SPECIALIX_IO8) },
2360 { }
2361};
2362MODULE_DEVICE_TABLE(pci, specialx_pci_tbl);
2363
Linus Torvalds1da177e2005-04-16 15:20:36 -07002364module_init(specialix_init_module);
2365module_exit(specialix_exit_module);
2366
2367MODULE_LICENSE("GPL");
Scott James Remnant5350d3b2009-04-06 17:33:11 +01002368MODULE_ALIAS_CHARDEV_MAJOR(SPECIALIX_NORMAL_MAJOR);