cyclades.c 110 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6 7 8 9 10 11
#undef	BLOCKMOVE
#define	Z_WAKE
#undef	Z_EXT_CHARS_IN_BUFFER

/*
 * This file contains the driver for the Cyclades async multiport
 * serial boards.
 *
 * Initially written by Randolph Bentson <bentson@grieg.seaslug.org>.
 * Modified and maintained by Marcio Saito <marcio@cyclades.com>.
 *
Jiri Slaby's avatar
Jiri Slaby committed
12
 * Copyright (C) 2007-2009 Jiri Slaby <jirislaby@gmail.com>
Linus Torvalds's avatar
Linus Torvalds committed
13 14 15 16 17
 *
 * Much of the design and some of the code came from serial.c
 * which was copyright (C) 1991, 1992  Linus Torvalds.  It was
 * extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92,
 * and then fixed as suggested by Michael K. Johnson 12/12/92.
18
 * Converted to pci probing and cleaned up by Jiri Slaby.
Linus Torvalds's avatar
Linus Torvalds committed
19 20 21
 *
 */

Jiri Slaby's avatar
Jiri Slaby committed
22
#define CY_VERSION	"2.6"
23

Linus Torvalds's avatar
Linus Torvalds committed
24 25 26 27
/* If you need to install more boards than NR_CARDS, change the constant
   in the definition below. No other change is necessary to support up to
   eight boards. Beyond that you'll have to extend cy_isa_addresses. */

28
#define NR_CARDS	4
Linus Torvalds's avatar
Linus Torvalds committed
29 30 31 32 33 34

/*
   If the total number of ports is larger than NR_PORTS, change this
   constant in the definition below. No other change is necessary to
   support more boards/ports. */

35
#define NR_PORTS	256
Linus Torvalds's avatar
Linus Torvalds committed
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53

#define ZO_V1	0
#define ZO_V2	1
#define ZE_V1	2

#define	SERIAL_PARANOIA_CHECK
#undef	CY_DEBUG_OPEN
#undef	CY_DEBUG_THROTTLE
#undef	CY_DEBUG_OTHER
#undef	CY_DEBUG_IO
#undef	CY_DEBUG_COUNT
#undef	CY_DEBUG_DTR
#undef	CY_DEBUG_INTERRUPTS
#undef	CY_16Y_HACK
#undef	CY_ENABLE_MONITORING
#undef	CY_PCI_DEBUG

/*
54
 * Include section
Linus Torvalds's avatar
Linus Torvalds committed
55 56 57 58 59 60 61 62
 */
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
63
#include <linux/tty_flip.h>
Linus Torvalds's avatar
Linus Torvalds committed
64 65 66 67 68 69 70 71 72 73 74 75
#include <linux/serial.h>
#include <linux/major.h>
#include <linux/string.h>
#include <linux/fcntl.h>
#include <linux/ptrace.h>
#include <linux/cyclades.h>
#include <linux/mm.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <linux/bitops.h>
76
#include <linux/firmware.h>
77
#include <linux/device.h>
78
#include <linux/slab.h>
Linus Torvalds's avatar
Linus Torvalds committed
79

80 81
#include <linux/io.h>
#include <linux/uaccess.h>
Linus Torvalds's avatar
Linus Torvalds committed
82 83 84 85 86 87

#include <linux/kernel.h>
#include <linux/pci.h>

#include <linux/stat.h>
#include <linux/proc_fs.h>
88
#include <linux/seq_file.h>
Linus Torvalds's avatar
Linus Torvalds committed
89

90
static void cy_send_xchar(struct tty_struct *tty, char ch);
Linus Torvalds's avatar
Linus Torvalds committed
91 92 93 94 95

#ifndef SERIAL_XMIT_SIZE
#define	SERIAL_XMIT_SIZE	(min(PAGE_SIZE, 4096))
#endif

96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
/* firmware stuff */
#define ZL_MAX_BLOCKS	16
#define DRIVER_VERSION	0x02010203
#define RAM_SIZE 0x80000

enum zblock_type {
	ZBLOCK_PRG = 0,
	ZBLOCK_FPGA = 1
};

struct zfile_header {
	char name[64];
	char date[32];
	char aux[32];
	u32 n_config;
	u32 config_offset;
	u32 n_blocks;
	u32 block_offset;
	u32 reserved[9];
} __attribute__ ((packed));

struct zfile_config {
	char name[64];
	u32 mailbox;
	u32 function;
	u32 n_blocks;
	u32 block_list[ZL_MAX_BLOCKS];
} __attribute__ ((packed));

struct zfile_block {
	u32 type;
	u32 file_offset;
	u32 ram_offset;
	u32 size;
} __attribute__ ((packed));

Linus Torvalds's avatar
Linus Torvalds committed
132 133 134 135 136 137 138 139 140 141 142
static struct tty_driver *cy_serial_driver;

#ifdef CONFIG_ISA
/* This is the address lookup table. The driver will probe for
   Cyclom-Y/ISA boards at all addresses in here. If you want the
   driver to probe addresses at a different address, add it to
   this table.  If the driver is probing some other board and
   causing problems, remove the offending address from this table.
*/

static unsigned int cy_isa_addresses[] = {
143 144 145 146 147 148 149 150 151
	0xD0000,
	0xD2000,
	0xD4000,
	0xD6000,
	0xD8000,
	0xDA000,
	0xDC000,
	0xDE000,
	0, 0, 0, 0, 0, 0, 0, 0
Linus Torvalds's avatar
Linus Torvalds committed
152
};
153

154
#define NR_ISA_ADDRS ARRAY_SIZE(cy_isa_addresses)
Linus Torvalds's avatar
Linus Torvalds committed
155

156 157
static long maddr[NR_CARDS];
static int irq[NR_CARDS];
Linus Torvalds's avatar
Linus Torvalds committed
158 159 160 161

module_param_array(maddr, long, NULL, 0);
module_param_array(irq, int, NULL, 0);

162
#endif				/* CONFIG_ISA */
Linus Torvalds's avatar
Linus Torvalds committed
163 164 165 166 167 168

/* This is the per-card data structure containing address, irq, number of
   channels, etc. This driver supports a maximum of NR_CARDS cards.
*/
static struct cyclades_card cy_card[NR_CARDS];

169
static int cy_next_channel;	/* next minor available */
Linus Torvalds's avatar
Linus Torvalds committed
170 171 172 173

/*
 * This is used to look up the divisor speeds and the timeouts
 * We're normally limited to 15 distinct baud rates.  The extra
Alan Cox's avatar
Alan Cox committed
174
 * are accessed via settings in info->port.flags.
Linus Torvalds's avatar
Linus Torvalds committed
175 176 177 178 179
 *      0,     1,     2,     3,     4,     5,     6,     7,     8,     9,
 *     10,    11,    12,    13,    14,    15,    16,    17,    18,    19,
 *                                               HI            VHI
 *     20
 */
Jiri Slaby's avatar
Jiri Slaby committed
180
static const int baud_table[] = {
181 182 183 184 185
	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200,
	1800, 2400, 4800, 9600, 19200, 38400, 57600, 76800, 115200, 150000,
	230400, 0
};

Jiri Slaby's avatar
Jiri Slaby committed
186
static const char baud_co_25[] = {	/* 25 MHz clock option table */
187 188 189 190 191 192
	/* value =>    00    01   02    03    04 */
	/* divide by    8    32   128   512  2048 */
	0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02,
	0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

Jiri Slaby's avatar
Jiri Slaby committed
193
static const char baud_bpr_25[] = {	/* 25 MHz baud rate period table */
194 195 196 197
	0x00, 0xf5, 0xa3, 0x6f, 0x5c, 0x51, 0xf5, 0xa3, 0x51, 0xa3,
	0x6d, 0x51, 0xa3, 0x51, 0xa3, 0x51, 0x36, 0x29, 0x1b, 0x15
};

Jiri Slaby's avatar
Jiri Slaby committed
198
static const char baud_co_60[] = {	/* 60 MHz clock option table (CD1400 J) */
199 200 201 202 203 204 205
	/* value =>    00    01   02    03    04 */
	/* divide by    8    32   128   512  2048 */
	0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03,
	0x03, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00
};

Jiri Slaby's avatar
Jiri Slaby committed
206
static const char baud_bpr_60[] = {	/* 60 MHz baud rate period table (CD1400 J) */
207 208 209 210 211
	0x00, 0x82, 0x21, 0xff, 0xdb, 0xc3, 0x92, 0x62, 0xc3, 0x62,
	0x41, 0xc3, 0x62, 0xc3, 0x62, 0xc3, 0x82, 0x62, 0x41, 0x32,
	0x21
};

Jiri Slaby's avatar
Jiri Slaby committed
212
static const char baud_cor3[] = {	/* receive threshold */
213 214 215 216
	0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
	0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07,
	0x07
};
Linus Torvalds's avatar
Linus Torvalds committed
217 218 219

/*
 * The Cyclades driver implements HW flow control as any serial driver.
220 221 222 223 224 225
 * The cyclades_port structure member rflow and the vector rflow_thr
 * allows us to take advantage of a special feature in the CD1400 to avoid
 * data loss even when the system interrupt latency is too high. These flags
 * are to be used only with very special applications. Setting these flags
 * requires the use of a special cable (DTR and RTS reversed). In the new
 * CD1400-based boards (rev. 6.00 or later), there is no need for special
Linus Torvalds's avatar
Linus Torvalds committed
226 227 228
 * cables.
 */

Jiri Slaby's avatar
Jiri Slaby committed
229
static const char rflow_thr[] = {	/* rflow threshold */
230 231 232 233
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
	0x0a
};
Linus Torvalds's avatar
Linus Torvalds committed
234 235 236 237

/*  The Cyclom-Ye has placed the sequential chips in non-sequential
 *  address order.  This look-up table overcomes that problem.
 */
238
static const unsigned int cy_chip_offset[] = { 0x0000,
239 240 241 242 243 244 245 246
	0x0400,
	0x0800,
	0x0C00,
	0x0200,
	0x0600,
	0x0A00,
	0x0E00
};
Linus Torvalds's avatar
Linus Torvalds committed
247 248 249 250

/* PCI related definitions */

#ifdef CONFIG_PCI
Jiri Slaby's avatar
Jiri Slaby committed
251
static const struct pci_device_id cy_pci_dev_id[] = {
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
	/* PCI < 1Mb */
	{ PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Y_Lo) },
	/* PCI > 1Mb */
	{ PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Y_Hi) },
	/* 4Y PCI < 1Mb */
	{ PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_4Y_Lo) },
	/* 4Y PCI > 1Mb */
	{ PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_4Y_Hi) },
	/* 8Y PCI < 1Mb */
	{ PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_8Y_Lo) },
	/* 8Y PCI > 1Mb */
	{ PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_8Y_Hi) },
	/* Z PCI < 1Mb */
	{ PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Z_Lo) },
	/* Z PCI > 1Mb */
	{ PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Z_Hi) },
268
	{ }			/* end of table */
269
};
270
MODULE_DEVICE_TABLE(pci, cy_pci_dev_id);
Linus Torvalds's avatar
Linus Torvalds committed
271 272 273
#endif

static void cy_start(struct tty_struct *);
274
static void cy_set_line_char(struct cyclades_port *, struct tty_struct *);
275
static int cyz_issue_cmd(struct cyclades_card *, __u32, __u8, __u32);
Linus Torvalds's avatar
Linus Torvalds committed
276 277
#ifdef CONFIG_ISA
static unsigned detect_isa_irq(void __iomem *);
278
#endif				/* CONFIG_ISA */
Linus Torvalds's avatar
Linus Torvalds committed
279 280 281 282 283 284 285

#ifndef CONFIG_CYZ_INTR
static void cyz_poll(unsigned long);

/* The Cyclades-Z polling cycle is defined by this variable */
static long cyz_polling_cycle = CZ_DEF_POLL;

286
static DEFINE_TIMER(cyz_timerlist, cyz_poll, 0, 0);
Linus Torvalds's avatar
Linus Torvalds committed
287

288
#else				/* CONFIG_CYZ_INTR */
Linus Torvalds's avatar
Linus Torvalds committed
289 290
static void cyz_rx_restart(unsigned long);
static struct timer_list cyz_rx_full_timer[NR_PORTS];
291
#endif				/* CONFIG_CYZ_INTR */
Linus Torvalds's avatar
Linus Torvalds committed
292

293
static void cyy_writeb(struct cyclades_port *port, u32 reg, u8 val)
294 295 296 297 298 299
{
	struct cyclades_card *card = port->card;

	cy_writeb(port->u.cyy.base_addr + (reg << card->bus_index), val);
}

300
static u8 cyy_readb(struct cyclades_port *port, u32 reg)
301 302 303 304 305 306
{
	struct cyclades_card *card = port->card;

	return readb(port->u.cyy.base_addr + (reg << card->bus_index));
}

307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
static inline bool cy_is_Z(struct cyclades_card *card)
{
	return card->num_chips == (unsigned int)-1;
}

static inline bool __cyz_fpga_loaded(struct RUNTIME_9060 __iomem *ctl_addr)
{
	return readl(&ctl_addr->init_ctrl) & (1 << 17);
}

static inline bool cyz_fpga_loaded(struct cyclades_card *card)
{
	return __cyz_fpga_loaded(card->ctl_addr.p9060);
}

322
static bool cyz_is_loaded(struct cyclades_card *card)
323 324 325 326 327 328 329
{
	struct FIRM_ID __iomem *fw_id = card->base_addr + ID_ADDRESS;

	return (card->hw_ver == ZO_V1 || cyz_fpga_loaded(card)) &&
			readl(&fw_id->signature) == ZFIRM_ID;
}

330
static int serial_paranoia_check(struct cyclades_port *info,
Jiri Slaby's avatar
Jiri Slaby committed
331
		const char *name, const char *routine)
Linus Torvalds's avatar
Linus Torvalds committed
332 333
{
#ifdef SERIAL_PARANOIA_CHECK
334
	if (!info) {
335 336
		printk(KERN_WARNING "cyc Warning: null cyclades_port for (%s) "
				"in %s\n", name, routine);
337 338 339 340
		return 1;
	}

	if (info->magic != CYCLADES_MAGIC) {
341 342
		printk(KERN_WARNING "cyc Warning: bad magic number for serial "
				"struct (%s) in %s\n", name, routine);
343 344
		return 1;
	}
Linus Torvalds's avatar
Linus Torvalds committed
345
#endif
346
	return 0;
Jiri Slaby's avatar
Jiri Slaby committed
347
}
Linus Torvalds's avatar
Linus Torvalds committed
348 349 350 351 352 353 354 355 356 357 358

/***********************************************************/
/********* Start of block of Cyclom-Y specific code ********/

/* This routine waits up to 1000 micro-seconds for the previous
   command to the Cirrus chip to complete and then issues the
   new command.  An error is returned if the previous command
   didn't finish within the time limit.

   This function is only called from inside spinlock-protected code.
 */
359
static int __cyy_issue_cmd(void __iomem *base_addr, u8 cmd, int index)
Linus Torvalds's avatar
Linus Torvalds committed
360
{
361
	void __iomem *ccr = base_addr + (CyCCR << index);
362
	unsigned int i;
Linus Torvalds's avatar
Linus Torvalds committed
363

364 365
	/* Check to see that the previous command has completed */
	for (i = 0; i < 100; i++) {
366
		if (readb(ccr) == 0)
367 368
			break;
		udelay(10L);
Linus Torvalds's avatar
Linus Torvalds committed
369
	}
370 371 372
	/* if the CCR never cleared, the previous command
	   didn't finish within the "reasonable time" */
	if (i == 100)
373
		return -1;
Linus Torvalds's avatar
Linus Torvalds committed
374

375
	/* Issue the new command */
376
	cy_writeb(ccr, cmd);
Linus Torvalds's avatar
Linus Torvalds committed
377

378
	return 0;
379 380 381 382 383 384 385
}

static inline int cyy_issue_cmd(struct cyclades_port *port, u8 cmd)
{
	return __cyy_issue_cmd(port->u.cyy.base_addr, cmd,
			port->card->bus_index);
}
Linus Torvalds's avatar
Linus Torvalds committed
386 387 388

#ifdef CONFIG_ISA
/* ISA interrupt detection code */
389
static unsigned detect_isa_irq(void __iomem *address)
Linus Torvalds's avatar
Linus Torvalds committed
390
{
391 392 393 394 395 396 397 398 399 400 401 402 403 404
	int irq;
	unsigned long irqs, flags;
	int save_xir, save_car;
	int index = 0;		/* IRQ probing is only for ISA */

	/* forget possible initially masked and pending IRQ */
	irq = probe_irq_off(probe_irq_on());

	/* Clear interrupts on the board first */
	cy_writeb(address + (Cy_ClrIntr << index), 0);
	/* Cy_ClrIntr is 0x1800 */

	irqs = probe_irq_on();
	/* Wait ... */
405
	msleep(5);
406 407 408 409

	/* Enable the Tx interrupts on the CD1400 */
	local_irq_save(flags);
	cy_writeb(address + (CyCAR << index), 0);
410
	__cyy_issue_cmd(address, CyCHAN_CTL | CyENB_XMTR, index);
411 412 413

	cy_writeb(address + (CyCAR << index), 0);
	cy_writeb(address + (CySRER << index),
414
		  readb(address + (CySRER << index)) | CyTxRdy);
415 416 417
	local_irq_restore(flags);

	/* Wait ... */
418
	msleep(5);
419 420 421 422 423

	/* Check which interrupt is in use */
	irq = probe_irq_off(irqs);

	/* Clean up */
424 425
	save_xir = (u_char) readb(address + (CyTIR << index));
	save_car = readb(address + (CyCAR << index));
426 427
	cy_writeb(address + (CyCAR << index), (save_xir & 0x3));
	cy_writeb(address + (CySRER << index),
428
		  readb(address + (CySRER << index)) & ~CyTxRdy);
429 430 431 432 433 434
	cy_writeb(address + (CyTIR << index), (save_xir & 0x3f));
	cy_writeb(address + (CyCAR << index), (save_car));
	cy_writeb(address + (Cy_ClrIntr << index), 0);
	/* Cy_ClrIntr is 0x1800 */

	return (irq > 0) ? irq : 0;
Linus Torvalds's avatar
Linus Torvalds committed
435
}
436
#endif				/* CONFIG_ISA */
Linus Torvalds's avatar
Linus Torvalds committed
437

438 439
static void cyy_chip_rx(struct cyclades_card *cinfo, int chip,
		void __iomem *base_addr)
440 441
{
	struct cyclades_port *info;
442
	struct tty_port *port;
443
	int len, index = cinfo->bus_index;
444
	u8 ivr, save_xir, channel, save_car, data, char_count;
445 446

#ifdef CY_DEBUG_INTERRUPTS
447
	printk(KERN_DEBUG "cyy_interrupt: rcvd intr, chip %d\n", chip);
448
#endif
449
	/* determine the channel & change to that context */
450 451
	save_xir = readb(base_addr + (CyRIR << index));
	channel = save_xir & CyIRChannel;
452
	info = &cinfo->ports[channel + chip * 4];
453
	port = &info->port;
454 455 456
	save_car = cyy_readb(info, CyCAR);
	cyy_writeb(info, CyCAR, save_xir);
	ivr = cyy_readb(info, CyRIVR) & CyIVRMask;
457 458

	/* there is an open port for this data */
459 460
	if (ivr == CyIVRRxEx) {	/* exception */
		data = cyy_readb(info, CyRDSR);
461 462 463 464 465 466 467 468 469 470 471 472 473 474 475

		/* For statistics only */
		if (data & CyBREAK)
			info->icount.brk++;
		else if (data & CyFRAME)
			info->icount.frame++;
		else if (data & CyPARITY)
			info->icount.parity++;
		else if (data & CyOVERRUN)
			info->icount.overrun++;

		if (data & info->ignore_status_mask) {
			info->icount.rx++;
			return;
		}
476
		if (tty_buffer_request_room(port, 1)) {
477 478
			if (data & info->read_status_mask) {
				if (data & CyBREAK) {
479
					tty_insert_flip_char(port,
480 481
						cyy_readb(info, CyRDSR),
						TTY_BREAK);
482
					info->icount.rx++;
483 484 485 486 487 488 489 490
					if (port->flags & ASYNC_SAK) {
						struct tty_struct *tty =
							tty_port_tty_get(port);
						if (tty) {
							do_SAK(tty);
							tty_kref_put(tty);
						}
					}
491
				} else if (data & CyFRAME) {
492
					tty_insert_flip_char(port,
493 494
						cyy_readb(info, CyRDSR),
						TTY_FRAME);
495 496 497 498
					info->icount.rx++;
					info->idle_stats.frame_errs++;
				} else if (data & CyPARITY) {
					/* Pieces of seven... */
499
					tty_insert_flip_char(port,
500 501
						cyy_readb(info, CyRDSR),
						TTY_PARITY);
502 503 504
					info->icount.rx++;
					info->idle_stats.parity_errs++;
				} else if (data & CyOVERRUN) {
505
					tty_insert_flip_char(port, 0,
506 507 508 509 510 511
							TTY_OVERRUN);
					info->icount.rx++;
					/* If the flip buffer itself is
					   overflowing, we still lose
					   the next incoming character.
					 */
512
					tty_insert_flip_char(port,
513 514
						cyy_readb(info, CyRDSR),
						TTY_FRAME);
515 516
					info->icount.rx++;
					info->idle_stats.overruns++;
517 518 519 520 521
				/* These two conditions may imply */
				/* a normal read should be done. */
				/* } else if(data & CyTIMEOUT) { */
				/* } else if(data & CySPECHAR) { */
				} else {
522
					tty_insert_flip_char(port, 0,
523 524
							TTY_NORMAL);
					info->icount.rx++;
525
				}
526
			} else {
527
				tty_insert_flip_char(port, 0, TTY_NORMAL);
528 529 530 531 532 533 534 535 536 537
				info->icount.rx++;
			}
		} else {
			/* there was a software buffer overrun and nothing
			 * could be done about it!!! */
			info->icount.buf_overrun++;
			info->idle_stats.overruns++;
		}
	} else {	/* normal character reception */
		/* load # chars available from the chip */
538
		char_count = cyy_readb(info, CyRDCR);
539 540

#ifdef CY_ENABLE_MONITORING
541 542 543 544 545
		++info->mon.int_count;
		info->mon.char_count += char_count;
		if (char_count > info->mon.char_max)
			info->mon.char_max = char_count;
		info->mon.char_last = char_count;
546
#endif
547
		len = tty_buffer_request_room(port, char_count);
548
		while (len--) {
549
			data = cyy_readb(info, CyRDSR);
550
			tty_insert_flip_char(port, data, TTY_NORMAL);
551 552
			info->idle_stats.recv_bytes++;
			info->icount.rx++;
553
#ifdef CY_16Y_HACK
554
			udelay(10L);
555 556
#endif
		}
557
		info->idle_stats.recv_idle = jiffies;
558
	}
559 560
	tty_schedule_flip(port);

561
	/* end of service */
562 563
	cyy_writeb(info, CyRIR, save_xir & 0x3f);
	cyy_writeb(info, CyCAR, save_car);
564
}
565

566
static void cyy_chip_tx(struct cyclades_card *cinfo, unsigned int chip,
567 568 569
		void __iomem *base_addr)
{
	struct cyclades_port *info;
570
	struct tty_struct *tty;
571 572
	int char_count, index = cinfo->bus_index;
	u8 save_xir, channel, save_car, outch;
573 574 575 576

	/* Since we only get here when the transmit buffer
	   is empty, we know we can always stuff a dozen
	   characters. */
577
#ifdef CY_DEBUG_INTERRUPTS
578
	printk(KERN_DEBUG "cyy_interrupt: xmit intr, chip %d\n", chip);
579 580
#endif

581
	/* determine the channel & change to that context */
582 583
	save_xir = readb(base_addr + (CyTIR << index));
	channel = save_xir & CyIRChannel;
584 585 586 587
	save_car = readb(base_addr + (CyCAR << index));
	cy_writeb(base_addr + (CyCAR << index), save_xir);

	info = &cinfo->ports[channel + chip * 4];
588 589
	tty = tty_port_tty_get(&info->port);
	if (tty == NULL) {
590
		cyy_writeb(info, CySRER, cyy_readb(info, CySRER) & ~CyTxRdy);
591 592
		goto end;
	}
593

594 595
	/* load the on-chip space for outbound data */
	char_count = info->xmit_fifo_size;
596

597 598
	if (info->x_char) {	/* send special char */
		outch = info->x_char;
599
		cyy_writeb(info, CyTDR, outch);
600 601 602 603
		char_count--;
		info->icount.tx++;
		info->x_char = 0;
	}
604

605 606
	if (info->breakon || info->breakoff) {
		if (info->breakon) {
607 608
			cyy_writeb(info, CyTDR, 0);
			cyy_writeb(info, CyTDR, 0x81);
609 610
			info->breakon = 0;
			char_count -= 2;
611
		}
612
		if (info->breakoff) {
613 614
			cyy_writeb(info, CyTDR, 0);
			cyy_writeb(info, CyTDR, 0x83);
615 616
			info->breakoff = 0;
			char_count -= 2;
617
		}
618
	}
619

620 621
	while (char_count-- > 0) {
		if (!info->xmit_cnt) {
622 623 624
			if (cyy_readb(info, CySRER) & CyTxMpty) {
				cyy_writeb(info, CySRER,
					cyy_readb(info, CySRER) & ~CyTxMpty);
625
			} else {
626 627
				cyy_writeb(info, CySRER, CyTxMpty |
					(cyy_readb(info, CySRER) & ~CyTxRdy));
628
			}
629 630
			goto done;
		}
Alan Cox's avatar
Alan Cox committed
631
		if (info->port.xmit_buf == NULL) {
632 633
			cyy_writeb(info, CySRER,
				cyy_readb(info, CySRER) & ~CyTxRdy);
634 635
			goto done;
		}
636
		if (tty->stopped || tty->hw_stopped) {
637 638
			cyy_writeb(info, CySRER,
				cyy_readb(info, CySRER) & ~CyTxRdy);
639 640 641 642 643 644 645 646 647 648 649
			goto done;
		}
		/* Because the Embedded Transmit Commands have been enabled,
		 * we must check to see if the escape character, NULL, is being
		 * sent. If it is, we must ensure that there is room for it to
		 * be doubled in the output stream.  Therefore we no longer
		 * advance the pointer when the character is fetched, but
		 * rather wait until after the check for a NULL output
		 * character. This is necessary because there may not be room
		 * for the two chars needed to send a NULL.)
		 */
Alan Cox's avatar
Alan Cox committed
650
		outch = info->port.xmit_buf[info->xmit_tail];
651 652 653 654
		if (outch) {
			info->xmit_cnt--;
			info->xmit_tail = (info->xmit_tail + 1) &
					(SERIAL_XMIT_SIZE - 1);
655
			cyy_writeb(info, CyTDR, outch);
656 657 658
			info->icount.tx++;
		} else {
			if (char_count > 1) {
659 660
				info->xmit_cnt--;
				info->xmit_tail = (info->xmit_tail + 1) &
661
					(SERIAL_XMIT_SIZE - 1);
662 663
				cyy_writeb(info, CyTDR, outch);
				cyy_writeb(info, CyTDR, 0);
664
				info->icount.tx++;
665
				char_count--;
666
			}
667 668 669
		}
	}

670
done:
671 672
	tty_wakeup(tty);
	tty_kref_put(tty);
673 674
end:
	/* end of service */
675 676
	cyy_writeb(info, CyTIR, save_xir & 0x3f);
	cyy_writeb(info, CyCAR, save_car);
677
}
678

679 680 681 682
static void cyy_chip_modem(struct cyclades_card *cinfo, int chip,
		void __iomem *base_addr)
{
	struct cyclades_port *info;
683
	struct tty_struct *tty;
684 685
	int index = cinfo->bus_index;
	u8 save_xir, channel, save_car, mdm_change, mdm_status;
686 687

	/* determine the channel & change to that context */
688 689
	save_xir = readb(base_addr + (CyMIR << index));
	channel = save_xir & CyIRChannel;
690
	info = &cinfo->ports[channel + chip * 4];
691 692
	save_car = cyy_readb(info, CyCAR);
	cyy_writeb(info, CyCAR, save_xir);
693

694 695
	mdm_change = cyy_readb(info, CyMISR);
	mdm_status = cyy_readb(info, CyMSVR1);
696

697 698
	tty = tty_port_tty_get(&info->port);
	if (!tty)
699 700 701 702 703 704 705 706 707 708 709 710 711
		goto end;

	if (mdm_change & CyANY_DELTA) {
		/* For statistics only */
		if (mdm_change & CyDCD)
			info->icount.dcd++;
		if (mdm_change & CyCTS)
			info->icount.cts++;
		if (mdm_change & CyDSR)
			info->icount.dsr++;
		if (mdm_change & CyRI)
			info->icount.rng++;

712
		wake_up_interruptible(&info->port.delta_msr_wait);
713 714
	}

715
	if ((mdm_change & CyDCD) && tty_port_check_carrier(&info->port)) {
716 717 718
		if (mdm_status & CyDCD)
			wake_up_interruptible(&info->port.open_wait);
		else
719
			tty_hangup(tty);
720
	}
721
	if ((mdm_change & CyCTS) && tty_port_cts_enabled(&info->port)) {
722
		if (tty->hw_stopped) {
723 724 725
			if (mdm_status & CyCTS) {
				/* cy_start isn't used
				   because... !!! */
726
				tty->hw_stopped = 0;
727 728
				cyy_writeb(info, CySRER,
					cyy_readb(info, CySRER) | CyTxRdy);
729
				tty_wakeup(tty);
730
			}
731 732 733 734
		} else {
			if (!(mdm_status & CyCTS)) {
				/* cy_stop isn't used
				   because ... !!! */
735
				tty->hw_stopped = 1;
736 737
				cyy_writeb(info, CySRER,
					cyy_readb(info, CySRER) & ~CyTxRdy);
738
			}
739 740
		}
	}
741 742 743 744
/*	if (mdm_change & CyDSR) {
	}
	if (mdm_change & CyRI) {
	}*/
745
	tty_kref_put(tty);
746 747
end:
	/* end of service */
748 749
	cyy_writeb(info, CyMIR, save_xir & 0x3f);
	cyy_writeb(info, CyCAR, save_car);
750 751
}

Linus Torvalds's avatar
Linus Torvalds committed
752 753 754 755
/* The real interrupt service routine is called
   whenever the card wants its hand held--chars
   received, out buffer empty, modem change, etc.
 */
756
static irqreturn_t cyy_interrupt(int irq, void *dev_id)
Linus Torvalds's avatar
Linus Torvalds committed
757
{
758
	int status;
759
	struct cyclades_card *cinfo = dev_id;
760
	void __iomem *base_addr, *card_base_addr;
761
	unsigned int chip, too_many, had_work;
762 763
	int index;

764
	if (unlikely(cinfo == NULL)) {
Linus Torvalds's avatar
Linus Torvalds committed
765
#ifdef CY_DEBUG_INTERRUPTS
766 767
		printk(KERN_DEBUG "cyy_interrupt: spurious interrupt %d\n",
				irq);
Linus Torvalds's avatar
Linus Torvalds committed
768
#endif
769 770 771 772 773 774
		return IRQ_NONE;	/* spurious interrupt */
	}

	card_base_addr = cinfo->base_addr;
	index = cinfo->bus_index;

775 776 777 778
	/* card was not initialized yet (e.g. DEBUG_SHIRQ) */
	if (unlikely(card_base_addr == NULL))
		return IRQ_HANDLED;

779 780 781 782 783 784 785 786 787 788 789
	/* This loop checks all chips in the card.  Make a note whenever
	   _any_ chip had some work to do, as this is considered an
	   indication that there will be more to do.  Only when no chip
	   has any work does this outermost loop exit.
	 */
	do {
		had_work = 0;
		for (chip = 0; chip < cinfo->num_chips; chip++) {
			base_addr = cinfo->base_addr +
					(cy_chip_offset[chip] << index);
			too_many = 0;
790
			while ((status = readb(base_addr +
791 792 793 794 795 796 797
						(CySVRR << index))) != 0x00) {
				had_work++;
			/* The purpose of the following test is to ensure that
			   no chip can monopolize the driver.  This forces the
			   chips to be checked in a round-robin fashion (after
			   draining each of a bunch (1000) of characters).
			 */
798
				if (1000 < too_many++)
799
					break;
800
				spin_lock(&cinfo->card_lock);
801 802 803 804 805 806
				if (status & CySRReceive) /* rx intr */
					cyy_chip_rx(cinfo, chip, base_addr);
				if (status & CySRTransmit) /* tx intr */
					cyy_chip_tx(cinfo, chip, base_addr);
				if (status & CySRModem) /* modem intr */
					cyy_chip_modem(cinfo, chip, base_addr);
807
				spin_unlock(&cinfo->card_lock);
808 809 810 811 812 813 814 815 816 817 818
			}
		}
	} while (had_work);

	/* clear interrupts */
	spin_lock(&cinfo->card_lock);
	cy_writeb(card_base_addr + (Cy_ClrIntr << index), 0);
	/* Cy_ClrIntr is 0x1800 */
	spin_unlock(&cinfo->card_lock);
	return IRQ_HANDLED;
}				/* cyy_interrupt */
Linus Torvalds's avatar
Linus Torvalds committed
819

820 821 822 823
static void cyy_change_rts_dtr(struct cyclades_port *info, unsigned int set,
		unsigned int clear)
{
	struct cyclades_card *card = info->card;
824
	int channel = info->line - card->first_line;
Jiri Slaby's avatar
Jiri Slaby committed
825
	u32 rts, dtr, msvrr, msvrd;
826 827 828

	channel &= 0x03;

Jiri Slaby's avatar
Jiri Slaby committed
829 830 831 832 833 834 835 836 837 838 839
	if (info->rtsdtr_inv) {
		msvrr = CyMSVR2;
		msvrd = CyMSVR1;
		rts = CyDTR;
		dtr = CyRTS;
	} else {
		msvrr = CyMSVR1;
		msvrd = CyMSVR2;
		rts = CyRTS;
		dtr = CyDTR;
	}
840
	if (set & TIOCM_RTS) {
841 842
		cyy_writeb(info, CyCAR, channel);
		cyy_writeb(info, msvrr, rts);
843 844
	}
	if (clear & TIOCM_RTS) {
845 846
		cyy_writeb(info, CyCAR, channel);
		cyy_writeb(info, msvrr, ~rts);
847 848
	}
	if (set & TIOCM_DTR) {
849 850
		cyy_writeb(info, CyCAR, channel);
		cyy_writeb(info, msvrd, dtr);
851 852 853
#ifdef CY_DEBUG_DTR
		printk(KERN_DEBUG "cyc:set_modem_info raising DTR\n");
		printk(KERN_DEBUG "     status: 0x%x, 0x%x\n",
854 855
			cyy_readb(info, CyMSVR1),
			cyy_readb(info, CyMSVR2));
856 857 858
#endif
	}
	if (clear & TIOCM_DTR) {
859 860
		cyy_writeb(info, CyCAR, channel);
		cyy_writeb(info, msvrd, ~dtr);
861 862 863
#ifdef CY_DEBUG_DTR
		printk(KERN_DEBUG "cyc:set_modem_info dropping DTR\n");
		printk(KERN_DEBUG "     status: 0x%x, 0x%x\n",
864 865
			cyy_readb(info, CyMSVR1),
			cyy_readb(info, CyMSVR2));
866 867 868 869
#endif
	}
}

Linus Torvalds's avatar
Linus Torvalds committed
870 871
/***********************************************************/
/********* End of block of Cyclom-Y specific code **********/
872
/******** Start of block of Cyclades-Z specific code *******/
Linus Torvalds's avatar
Linus Torvalds committed
873 874 875
/***********************************************************/

static int
876
cyz_fetch_msg(struct cyclades_card *cinfo,
877
		__u32 *channel, __u8 *cmd, __u32 *param)
Linus Torvalds's avatar
Linus Torvalds committed
878
{
879
	struct BOARD_CTRL __iomem *board_ctrl = cinfo->board_ctrl;
880 881
	unsigned long loc_doorbell;

882
	loc_doorbell = readl(&cinfo->ctl_addr.p9060->loc_doorbell);
883 884
	if (loc_doorbell) {
		*cmd = (char)(0xff & loc_doorbell);
885 886
		*channel = readl(&board_ctrl->fwcmd_channel);
		*param = (__u32) readl(&board_ctrl->fwcmd_param);
887
		cy_writel(&cinfo->ctl_addr.p9060->loc_doorbell, 0xffffffff);
888 889 890 891
		return 1;
	}
	return 0;
}				/* cyz_fetch_msg */
Linus Torvalds's avatar
Linus Torvalds committed
892 893

static int
894
cyz_issue_cmd(struct cyclades_card *cinfo,
895
		__u32 channel, __u8 cmd, __u32 param)
Linus Torvalds's avatar
Linus Torvalds committed
896
{
897
	struct BOARD_CTRL __iomem *board_ctrl = cinfo->board_ctrl;
898
	__u32 __iomem *pci_doorbell;
899
	unsigned int index;
900

901
	if (!cyz_is_loaded(cinfo))
902
		return -1;
903

904
	index = 0;
905
	pci_doorbell = &cinfo->ctl_addr.p9060->pci_doorbell;
906
	while ((readl(pci_doorbell) & 0xff) != 0) {
907
		if (index++ == 1000)
908
			return (int)(readl(pci_doorbell) & 0xff);
909 910 911 912 913 914
		udelay(50L);
	}
	cy_writel(&board_ctrl->hcmd_channel, channel);
	cy_writel(&board_ctrl->hcmd_param, param);
	cy_writel(pci_doorbell, (long)cmd);

915
	return 0;
916
}				/* cyz_issue_cmd */
Linus Torvalds's avatar
Linus Torvalds committed
917

918
static void cyz_handle_rx(struct cyclades_port *info)
Linus Torvalds's avatar
Linus Torvalds committed
919
{
920
	struct BUF_CTRL __iomem *buf_ctrl = info->u.cyz.buf_ctrl;
921
	struct cyclades_card *cinfo = info->card;
922
	struct tty_port *port = &info->port;
923
	unsigned int char_count;
924
	int len;
Linus Torvalds's avatar
Linus Torvalds committed
925
#ifdef BLOCKMOVE
926
	unsigned char *buf;
Linus Torvalds's avatar
Linus Torvalds committed
927
#else
928
	char data;
Linus Torvalds's avatar
Linus Torvalds committed
929
#endif
930
	__u32 rx_put, rx_get, new_rx_get, rx_bufsize, rx_bufaddr;
Linus Torvalds's avatar
Linus Torvalds committed
931

932 933 934 935
	rx_get = new_rx_get = readl(&buf_ctrl->rx_get);
	rx_put = readl(&buf_ctrl->rx_put);
	rx_bufsize = readl(&buf_ctrl->rx_bufsize);
	rx_bufaddr = readl(&buf_ctrl->rx_bufaddr);
936 937 938 939
	if (rx_put >= rx_get)
		char_count = rx_put - rx_get;
	else
		char_count = rx_put - rx_get + rx_bufsize;
Linus Torvalds's avatar
Linus Torvalds committed
940

941 942 943
	if (!char_count)
		return;

Linus Torvalds's avatar
Linus Torvalds committed
944
#ifdef CY_ENABLE_MONITORING
945 946 947 948 949
	info->mon.int_count++;
	info->mon.char_count += char_count;
	if (char_count > info->mon.char_max)
		info->mon.char_max = char_count;
	info->mon.char_last = char_count;
Linus Torvalds's avatar
Linus Torvalds committed
950
#endif
951

Linus Torvalds's avatar
Linus Torvalds committed
952
#ifdef BLOCKMOVE
953 954 955 956 957 958 959 960
	/* we'd like to use memcpy(t, f, n) and memset(s, c, count)
	   for performance, but because of buffer boundaries, there
	   may be several steps to the operation */
	while (1) {
		len = tty_prepare_flip_string(port, &buf,
				char_count);
		if (!len)
			break;
961

962 963
		len = min_t(unsigned int, min(len, char_count),
				rx_bufsize - new_rx_get);
964

965 966
		memcpy_fromio(buf, cinfo->base_addr +
				rx_bufaddr + new_rx_get, len);
967

968 969 970 971 972 973
		new_rx_get = (new_rx_get + len) &
				(rx_bufsize - 1);
		char_count -= len;
		info->icount.rx += len;
		info->idle_stats.recv_bytes += len;
	}
Linus Torvalds's avatar
Linus Torvalds committed
974
#else
975 976 977 978 979 980 981 982 983 984
	len = tty_buffer_request_room(port, char_count);
	while (len--) {
		data = readb(cinfo->base_addr + rx_bufaddr +
				new_rx_get);
		new_rx_get = (new_rx_get + 1) &
					(rx_bufsize - 1);
		tty_insert_flip_char(port, data, TTY_NORMAL);
		info->idle_stats.recv_bytes++;
		info->icount.rx++;
	}
Linus Torvalds's avatar
Linus Torvalds committed
985 986
#endif
#ifdef CONFIG_CYZ_INTR
987 988 989 990 991 992 993 994 995 996 997 998
	/* Recalculate the number of chars in the RX buffer and issue
	   a cmd in case it's higher than the RX high water mark */
	rx_put = readl(&buf_ctrl->rx_put);
	if (rx_put >= rx_get)
		char_count = rx_put - rx_get;
	else
		char_count = rx_put - rx_get + rx_bufsize;
	if (char_count >= readl(&buf_ctrl->rx_threshold) &&
			!timer_pending(&cyz_rx_full_timer[
					info->line]))
		mod_timer(&cyz_rx_full_timer[info->line],
				jiffies + 1);
Linus Torvalds's avatar
Linus Torvalds committed
999
#endif
1000 1001 1002 1003 1004
	info->idle_stats.recv_idle = jiffies;
	tty_schedule_flip(&info->port);

	/* Update rx_get */
	cy_writel(&buf_ctrl->rx_get, new_rx_get);
Linus Torvalds's avatar
Linus Torvalds committed
1005 1006
}

1007
static void cyz_handle_tx(struct cyclades_port *info)
Linus Torvalds's avatar
Linus Torvalds committed
1008
{
1009
	struct BUF_CTRL __iomem *buf_ctrl = info->u.cyz.buf_ctrl;
1010
	struct cyclades_card *cinfo = info->card;
1011
	struct tty_struct *tty;
1012 1013
	u8 data;
	unsigned int char_count;
Linus Torvalds's avatar
Linus Torvalds committed
1014
#ifdef BLOCKMOVE
1015
	int small_count;
Linus Torvalds's avatar
Linus Torvalds committed
1016
#endif
1017
	__u32 tx_put, tx_get, tx_bufsize, tx_bufaddr;
Linus Torvalds's avatar
Linus Torvalds committed
1018

1019 1020
	if (info->xmit_cnt <= 0)	/* Nothing to transmit */
		return;
Linus Torvalds's avatar
Linus Torvalds committed
1021

1022 1023 1024 1025
	tx_get = readl(&buf_ctrl->tx_get);
	tx_put = readl(&buf_ctrl->tx_put);
	tx_bufsize = readl(&buf_ctrl->tx_bufsize);
	tx_bufaddr = readl(&buf_ctrl->tx_bufaddr);
1026 1027 1028 1029
	if (tx_put >= tx_get)
		char_count = tx_get - tx_put - 1 + tx_bufsize;
	else
		char_count = tx_get - tx_put - 1;
Linus Torvalds's avatar
Linus Torvalds committed
1030

1031 1032 1033 1034 1035 1036
	if (!char_count)
		return;
		
	tty = tty_port_tty_get(&info->port);
	if (tty == NULL)
		goto ztxdone;
Linus Torvalds's avatar
Linus Torvalds committed
1037

1038 1039
	if (info->x_char) {	/* send special char */
		data = info->x_char;
Linus Torvalds's avatar
Linus Torvalds committed
1040

1041 1042 1043 1044 1045 1046
		cy_writeb(cinfo->base_addr + tx_bufaddr + tx_put, data);
		tx_put = (tx_put + 1) & (tx_bufsize - 1);
		info->x_char = 0;
		char_count--;
		info->icount.tx++;
	}
Linus Torvalds's avatar
Linus Torvalds committed
1047
#ifdef BLOCKMOVE
1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064
	while (0 < (small_count = min_t(unsigned int,
			tx_bufsize - tx_put, min_t(unsigned int,
				(SERIAL_XMIT_SIZE - info->xmit_tail),
				min_t(unsigned int, info->xmit_cnt,
					char_count))))) {

		memcpy_toio((char *)(cinfo->base_addr + tx_bufaddr + tx_put),
				&info->port.xmit_buf[info->xmit_tail],
				small_count);

		tx_put = (tx_put + small_count) & (tx_bufsize - 1);
		char_count -= small_count;
		info->icount.tx += small_count;
		info->xmit_cnt -= small_count;
		info->xmit_tail = (info->xmit_tail + small_count) &
				(SERIAL_XMIT_SIZE - 1);
	}
Linus Torvalds's avatar
Linus Torvalds committed
1065
#else
1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076
	while (info->xmit_cnt && char_count) {
		data = info->port.xmit_buf[info->xmit_tail];
		info->xmit_cnt--;
		info->xmit_tail = (info->xmit_tail + 1) &
				(SERIAL_XMIT_SIZE - 1);

		cy_writeb(cinfo->base_addr + tx_bufaddr + tx_put, data);
		tx_put = (tx_put + 1) & (tx_bufsize - 1);
		char_count--;
		info->icount.tx++;
	}
Linus Torvalds's avatar
Linus Torvalds committed
1077
#endif
1078 1079
	tty_wakeup(tty);
	tty_kref_put(tty);
1080
ztxdone:
1081 1082
	/* Update tx_put */
	cy_writel(&buf_ctrl->tx_put, tx_put);
Linus Torvalds's avatar
Linus Torvalds committed
1083 1084
}

1085
static void cyz_handle_cmd(struct cyclades_card *cinfo)
Linus Torvalds's avatar
Linus Torvalds committed
1086
{
1087
	struct BOARD_CTRL __iomem *board_ctrl = cinfo->board_ctrl;
1088
	struct cyclades_port *info;
1089
	__u32 channel, param, fw_ver;
1090
	__u8 cmd;
1091 1092 1093
	int special_count;
	int delta_count;

1094
	fw_ver = readl(&board_ctrl->fw_version);
1095 1096 1097 1098

	while (cyz_fetch_msg(cinfo, &channel, &cmd, &param) == 1) {
		special_count = 0;
		delta_count = 0;
1099
		info = &cinfo->ports[channel];
1100

1101 1102
		switch (cmd) {
		case C_CM_PR_ERROR:
1103
			tty_insert_flip_char(&info->port, 0, TTY_PARITY);
1104 1105 1106 1107
			info->icount.rx++;
			special_count++;
			break;
		case C_CM_FR_ERROR:
1108
			tty_insert_flip_char(&info->port, 0, TTY_FRAME);
1109 1110 1111 1112
			info->icount.rx++;
			special_count++;
			break;
		case C_CM_RXBRK:
1113
			tty_insert_flip_char(&info->port, 0, TTY_BREAK);
1114 1115 1116 1117 1118 1119
			info->icount.rx++;
			special_count++;
			break;
		case C_CM_MDCD:
			info->icount.dcd++;
			delta_count++;
1120
			if (tty_port_check_carrier(&info->port)) {
1121 1122
				u32 dcd = fw_ver > 241 ? param :
					readl(&info->u.cyz.ch_ctrl->rs_status);
1123
				if (dcd & C_RS_DCD)
Alan Cox's avatar
Alan Cox committed
1124
					wake_up_interruptible(&info->port.open_wait);
1125 1126
				else
					tty_port_tty_hangup(&info->port, false);
1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140
			}
			break;
		case C_CM_MCTS:
			info->icount.cts++;
			delta_count++;
			break;
		case C_CM_MRI:
			info->icount.rng++;
			delta_count++;
			break;
		case C_CM_MDSR:
			info->icount.dsr++;
			delta_count++;
			break;
Linus Torvalds's avatar
Linus Torvalds committed
1141
#ifdef Z_WAKE
1142
		case C_CM_IOCTLW:
1143
			complete(&info->shutdown_wait);
1144
			break;
Linus Torvalds's avatar
Linus Torvalds committed
1145 1146
#endif
#ifdef CONFIG_CYZ_INTR
1147 1148 1149 1150
		case C_CM_RXHIWM:
		case C_CM_RXNNDT:
		case C_CM_INTBACK2:
			/* Reception Interrupt */
Linus Torvalds's avatar
Linus Torvalds committed
1151
#ifdef CY_DEBUG_INTERRUPTS
1152 1153
			printk(KERN_DEBUG "cyz_interrupt: rcvd intr, card %d, "
					"port %ld\n", info->card, channel);
Linus Torvalds's avatar
Linus Torvalds committed
1154
#endif
1155
			cyz_handle_rx(info);
1156 1157 1158 1159 1160
			break;
		case C_CM_TXBEMPTY:
		case C_CM_TXLOWWM:
		case C_CM_INTBACK:
			/* Transmission Interrupt */
Linus Torvalds's avatar
Linus Torvalds committed
1161
#ifdef CY_DEBUG_INTERRUPTS
1162 1163
			printk(KERN_DEBUG "cyz_interrupt: xmit intr, card %d, "
					"port %ld\n", info->card, channel);
Linus Torvalds's avatar
Linus Torvalds committed
1164
#endif
1165
			cyz_handle_tx(info);
1166 1167 1168 1169 1170 1171 1172 1173 1174
			break;
#endif				/* CONFIG_CYZ_INTR */
		case C_CM_FATAL:
			/* should do something with this !!! */
			break;
		default:
			break;
		}
		if (delta_count)
1175
			wake_up_interruptible(&info->port.delta_msr_wait);
1176
		if (special_count)
1177
			tty_schedule_flip(&info->port);
Linus Torvalds's avatar
Linus Torvalds committed
1178 1179 1180 1181
	}
}

#ifdef CONFIG_CYZ_INTR
1182
static irqreturn_t cyz_interrupt(int irq, void *dev_id)
Linus Torvalds's avatar
Linus Torvalds committed
1183
{
1184
	struct cyclades_card *cinfo = dev_id;
Linus Torvalds's avatar
Linus Torvalds committed
1185

1186
	if (unlikely(!cyz_is_loaded(cinfo))) {
Linus Torvalds's avatar
Linus Torvalds committed
1187
#ifdef CY_DEBUG_INTERRUPTS
1188 1189
		printk(KERN_DEBUG "cyz_interrupt: board not yet loaded "
				"(IRQ%d).\n", irq);
Linus Torvalds's avatar
Linus Torvalds committed
1190
#endif
1191 1192
		return IRQ_NONE;
	}
Linus Torvalds's avatar
Linus Torvalds committed
1193

1194 1195
	/* Handle the interrupts */
	cyz_handle_cmd(cinfo);
Linus Torvalds's avatar
Linus Torvalds committed
1196

1197 1198
	return IRQ_HANDLED;
}				/* cyz_interrupt */
Linus Torvalds's avatar
Linus Torvalds committed
1199

1200
static void cyz_rx_restart(unsigned long arg)
Linus Torvalds's avatar
Linus Torvalds committed
1201
{
1202
	struct cyclades_port *info = (struct cyclades_port *)arg;
1203
	struct cyclades_card *card = info->card;
1204
	int retval;
1205
	__u32 channel = info->line - card->first_line;
1206 1207
	unsigned long flags;

1208
	spin_lock_irqsave(&card->card_lock, flags);
1209
	retval = cyz_issue_cmd(card, channel, C_CM_INTBACK2, 0L);
1210
	if (retval != 0) {
1211
		printk(KERN_ERR "cyc:cyz_rx_restart retval on ttyC%d was %x\n",
1212 1213
			info->line, retval);
	}
1214
	spin_unlock_irqrestore(&card->card_lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
1215 1216
}

1217
#else				/* CONFIG_CYZ_INTR */
Linus Torvalds's avatar
Linus Torvalds committed
1218

1219
static void cyz_poll(unsigned long arg)
Linus Torvalds's avatar
Linus Torvalds committed
1220
{
1221 1222
	struct cyclades_card *cinfo;
	struct cyclades_port *info;
1223
	unsigned long expires = jiffies + HZ;
1224
	unsigned int port, card;
Linus Torvalds's avatar
Linus Torvalds committed
1225

1226 1227 1228
	for (card = 0; card < NR_CARDS; card++) {
		cinfo = &cy_card[card];

1229
		if (!cy_is_Z(cinfo))
1230
			continue;
1231
		if (!cyz_is_loaded(cinfo))
1232 1233
			continue;

Linus Torvalds's avatar
Linus Torvalds committed
1234
	/* Skip first polling cycle to avoid racing conditions with the FW */
1235 1236 1237 1238
		if (!cinfo->intr_enabled) {
			cinfo->intr_enabled = 1;
			continue;
		}
Linus Torvalds's avatar
Linus Torvalds committed
1239

1240
		cyz_handle_cmd(cinfo);
Linus Torvalds's avatar
Linus Torvalds committed
1241

1242
		for (port = 0; port < cinfo->nports; port++) {
1243
			info = &cinfo->ports[port];
1244

1245
			if (!info->throttle)
1246 1247
				cyz_handle_rx(info);
			cyz_handle_tx(info);
1248 1249
		}
		/* poll every 'cyz_polling_cycle' period */
1250
		expires = jiffies + cyz_polling_cycle;
Linus Torvalds's avatar
Linus Torvalds committed
1251
	}
1252
	mod_timer(&cyz_timerlist, expires);
1253
}				/* cyz_poll */
Linus Torvalds's avatar
Linus Torvalds committed
1254

1255
#endif				/* CONFIG_CYZ_INTR */
Linus Torvalds's avatar
Linus Torvalds committed
1256 1257 1258 1259 1260 1261 1262

/********** End of block of Cyclades-Z specific code *********/
/***********************************************************/

/* This is called whenever a port becomes active;
   interrupts are enabled and DTR & RTS are turned on.
 */
1263
static int cy_startup(struct cyclades_port *info, struct tty_struct *tty)
Linus Torvalds's avatar
Linus Torvalds committed
1264
{
1265
	struct cyclades_card *card;