18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#undef	BLOCKMOVE
38c2ecf20Sopenharmony_ci#define	Z_WAKE
48c2ecf20Sopenharmony_ci#undef	Z_EXT_CHARS_IN_BUFFER
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci/*
78c2ecf20Sopenharmony_ci * This file contains the driver for the Cyclades async multiport
88c2ecf20Sopenharmony_ci * serial boards.
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * Initially written by Randolph Bentson <bentson@grieg.seaslug.org>.
118c2ecf20Sopenharmony_ci * Modified and maintained by Marcio Saito <marcio@cyclades.com>.
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * Copyright (C) 2007-2009 Jiri Slaby <jirislaby@gmail.com>
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * Much of the design and some of the code came from serial.c
168c2ecf20Sopenharmony_ci * which was copyright (C) 1991, 1992  Linus Torvalds.  It was
178c2ecf20Sopenharmony_ci * extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92,
188c2ecf20Sopenharmony_ci * and then fixed as suggested by Michael K. Johnson 12/12/92.
198c2ecf20Sopenharmony_ci * Converted to pci probing and cleaned up by Jiri Slaby.
208c2ecf20Sopenharmony_ci *
218c2ecf20Sopenharmony_ci */
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define CY_VERSION	"2.6"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/* If you need to install more boards than NR_CARDS, change the constant
268c2ecf20Sopenharmony_ci   in the definition below. No other change is necessary to support up to
278c2ecf20Sopenharmony_ci   eight boards. Beyond that you'll have to extend cy_isa_addresses. */
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define NR_CARDS	4
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci/*
328c2ecf20Sopenharmony_ci   If the total number of ports is larger than NR_PORTS, change this
338c2ecf20Sopenharmony_ci   constant in the definition below. No other change is necessary to
348c2ecf20Sopenharmony_ci   support more boards/ports. */
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define NR_PORTS	256
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define ZO_V1	0
398c2ecf20Sopenharmony_ci#define ZO_V2	1
408c2ecf20Sopenharmony_ci#define ZE_V1	2
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#define	SERIAL_PARANOIA_CHECK
438c2ecf20Sopenharmony_ci#undef	CY_DEBUG_OPEN
448c2ecf20Sopenharmony_ci#undef	CY_DEBUG_THROTTLE
458c2ecf20Sopenharmony_ci#undef	CY_DEBUG_OTHER
468c2ecf20Sopenharmony_ci#undef	CY_DEBUG_IO
478c2ecf20Sopenharmony_ci#undef	CY_DEBUG_COUNT
488c2ecf20Sopenharmony_ci#undef	CY_DEBUG_DTR
498c2ecf20Sopenharmony_ci#undef	CY_DEBUG_INTERRUPTS
508c2ecf20Sopenharmony_ci#undef	CY_16Y_HACK
518c2ecf20Sopenharmony_ci#undef	CY_ENABLE_MONITORING
528c2ecf20Sopenharmony_ci#undef	CY_PCI_DEBUG
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci/*
558c2ecf20Sopenharmony_ci * Include section
568c2ecf20Sopenharmony_ci */
578c2ecf20Sopenharmony_ci#include <linux/module.h>
588c2ecf20Sopenharmony_ci#include <linux/errno.h>
598c2ecf20Sopenharmony_ci#include <linux/signal.h>
608c2ecf20Sopenharmony_ci#include <linux/sched.h>
618c2ecf20Sopenharmony_ci#include <linux/timer.h>
628c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
638c2ecf20Sopenharmony_ci#include <linux/tty.h>
648c2ecf20Sopenharmony_ci#include <linux/tty_flip.h>
658c2ecf20Sopenharmony_ci#include <linux/serial.h>
668c2ecf20Sopenharmony_ci#include <linux/major.h>
678c2ecf20Sopenharmony_ci#include <linux/string.h>
688c2ecf20Sopenharmony_ci#include <linux/fcntl.h>
698c2ecf20Sopenharmony_ci#include <linux/ptrace.h>
708c2ecf20Sopenharmony_ci#include <linux/cyclades.h>
718c2ecf20Sopenharmony_ci#include <linux/mm.h>
728c2ecf20Sopenharmony_ci#include <linux/ioport.h>
738c2ecf20Sopenharmony_ci#include <linux/init.h>
748c2ecf20Sopenharmony_ci#include <linux/delay.h>
758c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
768c2ecf20Sopenharmony_ci#include <linux/bitops.h>
778c2ecf20Sopenharmony_ci#include <linux/firmware.h>
788c2ecf20Sopenharmony_ci#include <linux/device.h>
798c2ecf20Sopenharmony_ci#include <linux/slab.h>
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci#include <linux/io.h>
828c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci#include <linux/kernel.h>
858c2ecf20Sopenharmony_ci#include <linux/pci.h>
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci#include <linux/stat.h>
888c2ecf20Sopenharmony_ci#include <linux/proc_fs.h>
898c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic void cy_send_xchar(struct tty_struct *tty, char ch);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci#ifndef SERIAL_XMIT_SIZE
948c2ecf20Sopenharmony_ci#define	SERIAL_XMIT_SIZE	(min(PAGE_SIZE, 4096))
958c2ecf20Sopenharmony_ci#endif
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci/* firmware stuff */
988c2ecf20Sopenharmony_ci#define ZL_MAX_BLOCKS	16
998c2ecf20Sopenharmony_ci#define DRIVER_VERSION	0x02010203
1008c2ecf20Sopenharmony_ci#define RAM_SIZE 0x80000
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cienum zblock_type {
1038c2ecf20Sopenharmony_ci	ZBLOCK_PRG = 0,
1048c2ecf20Sopenharmony_ci	ZBLOCK_FPGA = 1
1058c2ecf20Sopenharmony_ci};
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistruct zfile_header {
1088c2ecf20Sopenharmony_ci	char name[64];
1098c2ecf20Sopenharmony_ci	char date[32];
1108c2ecf20Sopenharmony_ci	char aux[32];
1118c2ecf20Sopenharmony_ci	u32 n_config;
1128c2ecf20Sopenharmony_ci	u32 config_offset;
1138c2ecf20Sopenharmony_ci	u32 n_blocks;
1148c2ecf20Sopenharmony_ci	u32 block_offset;
1158c2ecf20Sopenharmony_ci	u32 reserved[9];
1168c2ecf20Sopenharmony_ci} __attribute__ ((packed));
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistruct zfile_config {
1198c2ecf20Sopenharmony_ci	char name[64];
1208c2ecf20Sopenharmony_ci	u32 mailbox;
1218c2ecf20Sopenharmony_ci	u32 function;
1228c2ecf20Sopenharmony_ci	u32 n_blocks;
1238c2ecf20Sopenharmony_ci	u32 block_list[ZL_MAX_BLOCKS];
1248c2ecf20Sopenharmony_ci} __attribute__ ((packed));
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistruct zfile_block {
1278c2ecf20Sopenharmony_ci	u32 type;
1288c2ecf20Sopenharmony_ci	u32 file_offset;
1298c2ecf20Sopenharmony_ci	u32 ram_offset;
1308c2ecf20Sopenharmony_ci	u32 size;
1318c2ecf20Sopenharmony_ci} __attribute__ ((packed));
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic struct tty_driver *cy_serial_driver;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci#ifdef CONFIG_ISA
1368c2ecf20Sopenharmony_ci/* This is the address lookup table. The driver will probe for
1378c2ecf20Sopenharmony_ci   Cyclom-Y/ISA boards at all addresses in here. If you want the
1388c2ecf20Sopenharmony_ci   driver to probe addresses at a different address, add it to
1398c2ecf20Sopenharmony_ci   this table.  If the driver is probing some other board and
1408c2ecf20Sopenharmony_ci   causing problems, remove the offending address from this table.
1418c2ecf20Sopenharmony_ci*/
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic unsigned int cy_isa_addresses[] = {
1448c2ecf20Sopenharmony_ci	0xD0000,
1458c2ecf20Sopenharmony_ci	0xD2000,
1468c2ecf20Sopenharmony_ci	0xD4000,
1478c2ecf20Sopenharmony_ci	0xD6000,
1488c2ecf20Sopenharmony_ci	0xD8000,
1498c2ecf20Sopenharmony_ci	0xDA000,
1508c2ecf20Sopenharmony_ci	0xDC000,
1518c2ecf20Sopenharmony_ci	0xDE000,
1528c2ecf20Sopenharmony_ci	0, 0, 0, 0, 0, 0, 0, 0
1538c2ecf20Sopenharmony_ci};
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci#define NR_ISA_ADDRS ARRAY_SIZE(cy_isa_addresses)
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistatic long maddr[NR_CARDS];
1588c2ecf20Sopenharmony_cistatic int irq[NR_CARDS];
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cimodule_param_hw_array(maddr, long, iomem, NULL, 0);
1618c2ecf20Sopenharmony_cimodule_param_hw_array(irq, int, irq, NULL, 0);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci#endif				/* CONFIG_ISA */
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci/* This is the per-card data structure containing address, irq, number of
1668c2ecf20Sopenharmony_ci   channels, etc. This driver supports a maximum of NR_CARDS cards.
1678c2ecf20Sopenharmony_ci*/
1688c2ecf20Sopenharmony_cistatic struct cyclades_card cy_card[NR_CARDS];
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistatic int cy_next_channel;	/* next minor available */
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci/*
1738c2ecf20Sopenharmony_ci * This is used to look up the divisor speeds and the timeouts
1748c2ecf20Sopenharmony_ci * We're normally limited to 15 distinct baud rates.  The extra
1758c2ecf20Sopenharmony_ci * are accessed via settings in info->port.flags.
1768c2ecf20Sopenharmony_ci *      0,     1,     2,     3,     4,     5,     6,     7,     8,     9,
1778c2ecf20Sopenharmony_ci *     10,    11,    12,    13,    14,    15,    16,    17,    18,    19,
1788c2ecf20Sopenharmony_ci *                                               HI            VHI
1798c2ecf20Sopenharmony_ci *     20
1808c2ecf20Sopenharmony_ci */
1818c2ecf20Sopenharmony_cistatic const int baud_table[] = {
1828c2ecf20Sopenharmony_ci	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200,
1838c2ecf20Sopenharmony_ci	1800, 2400, 4800, 9600, 19200, 38400, 57600, 76800, 115200, 150000,
1848c2ecf20Sopenharmony_ci	230400, 0
1858c2ecf20Sopenharmony_ci};
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_cistatic const char baud_co_25[] = {	/* 25 MHz clock option table */
1888c2ecf20Sopenharmony_ci	/* value =>    00    01   02    03    04 */
1898c2ecf20Sopenharmony_ci	/* divide by    8    32   128   512  2048 */
1908c2ecf20Sopenharmony_ci	0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02,
1918c2ecf20Sopenharmony_ci	0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1928c2ecf20Sopenharmony_ci};
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cistatic const char baud_bpr_25[] = {	/* 25 MHz baud rate period table */
1958c2ecf20Sopenharmony_ci	0x00, 0xf5, 0xa3, 0x6f, 0x5c, 0x51, 0xf5, 0xa3, 0x51, 0xa3,
1968c2ecf20Sopenharmony_ci	0x6d, 0x51, 0xa3, 0x51, 0xa3, 0x51, 0x36, 0x29, 0x1b, 0x15
1978c2ecf20Sopenharmony_ci};
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cistatic const char baud_co_60[] = {	/* 60 MHz clock option table (CD1400 J) */
2008c2ecf20Sopenharmony_ci	/* value =>    00    01   02    03    04 */
2018c2ecf20Sopenharmony_ci	/* divide by    8    32   128   512  2048 */
2028c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03,
2038c2ecf20Sopenharmony_ci	0x03, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
2048c2ecf20Sopenharmony_ci	0x00
2058c2ecf20Sopenharmony_ci};
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cistatic const char baud_bpr_60[] = {	/* 60 MHz baud rate period table (CD1400 J) */
2088c2ecf20Sopenharmony_ci	0x00, 0x82, 0x21, 0xff, 0xdb, 0xc3, 0x92, 0x62, 0xc3, 0x62,
2098c2ecf20Sopenharmony_ci	0x41, 0xc3, 0x62, 0xc3, 0x62, 0xc3, 0x82, 0x62, 0x41, 0x32,
2108c2ecf20Sopenharmony_ci	0x21
2118c2ecf20Sopenharmony_ci};
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_cistatic const char baud_cor3[] = {	/* receive threshold */
2148c2ecf20Sopenharmony_ci	0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
2158c2ecf20Sopenharmony_ci	0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07,
2168c2ecf20Sopenharmony_ci	0x07
2178c2ecf20Sopenharmony_ci};
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci/*
2208c2ecf20Sopenharmony_ci * The Cyclades driver implements HW flow control as any serial driver.
2218c2ecf20Sopenharmony_ci * The cyclades_port structure member rflow and the vector rflow_thr
2228c2ecf20Sopenharmony_ci * allows us to take advantage of a special feature in the CD1400 to avoid
2238c2ecf20Sopenharmony_ci * data loss even when the system interrupt latency is too high. These flags
2248c2ecf20Sopenharmony_ci * are to be used only with very special applications. Setting these flags
2258c2ecf20Sopenharmony_ci * requires the use of a special cable (DTR and RTS reversed). In the new
2268c2ecf20Sopenharmony_ci * CD1400-based boards (rev. 6.00 or later), there is no need for special
2278c2ecf20Sopenharmony_ci * cables.
2288c2ecf20Sopenharmony_ci */
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cistatic const char rflow_thr[] = {	/* rflow threshold */
2318c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2328c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
2338c2ecf20Sopenharmony_ci	0x0a
2348c2ecf20Sopenharmony_ci};
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci/*  The Cyclom-Ye has placed the sequential chips in non-sequential
2378c2ecf20Sopenharmony_ci *  address order.  This look-up table overcomes that problem.
2388c2ecf20Sopenharmony_ci */
2398c2ecf20Sopenharmony_cistatic const unsigned int cy_chip_offset[] = { 0x0000,
2408c2ecf20Sopenharmony_ci	0x0400,
2418c2ecf20Sopenharmony_ci	0x0800,
2428c2ecf20Sopenharmony_ci	0x0C00,
2438c2ecf20Sopenharmony_ci	0x0200,
2448c2ecf20Sopenharmony_ci	0x0600,
2458c2ecf20Sopenharmony_ci	0x0A00,
2468c2ecf20Sopenharmony_ci	0x0E00
2478c2ecf20Sopenharmony_ci};
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci/* PCI related definitions */
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI
2528c2ecf20Sopenharmony_cistatic const struct pci_device_id cy_pci_dev_id[] = {
2538c2ecf20Sopenharmony_ci	/* PCI < 1Mb */
2548c2ecf20Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Y_Lo) },
2558c2ecf20Sopenharmony_ci	/* PCI > 1Mb */
2568c2ecf20Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Y_Hi) },
2578c2ecf20Sopenharmony_ci	/* 4Y PCI < 1Mb */
2588c2ecf20Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_4Y_Lo) },
2598c2ecf20Sopenharmony_ci	/* 4Y PCI > 1Mb */
2608c2ecf20Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_4Y_Hi) },
2618c2ecf20Sopenharmony_ci	/* 8Y PCI < 1Mb */
2628c2ecf20Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_8Y_Lo) },
2638c2ecf20Sopenharmony_ci	/* 8Y PCI > 1Mb */
2648c2ecf20Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_8Y_Hi) },
2658c2ecf20Sopenharmony_ci	/* Z PCI < 1Mb */
2668c2ecf20Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Z_Lo) },
2678c2ecf20Sopenharmony_ci	/* Z PCI > 1Mb */
2688c2ecf20Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Z_Hi) },
2698c2ecf20Sopenharmony_ci	{ }			/* end of table */
2708c2ecf20Sopenharmony_ci};
2718c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, cy_pci_dev_id);
2728c2ecf20Sopenharmony_ci#endif
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_cistatic void cy_start(struct tty_struct *);
2758c2ecf20Sopenharmony_cistatic void cy_set_line_char(struct cyclades_port *, struct tty_struct *);
2768c2ecf20Sopenharmony_cistatic int cyz_issue_cmd(struct cyclades_card *, __u32, __u8, __u32);
2778c2ecf20Sopenharmony_ci#ifdef CONFIG_ISA
2788c2ecf20Sopenharmony_cistatic unsigned detect_isa_irq(void __iomem *);
2798c2ecf20Sopenharmony_ci#endif				/* CONFIG_ISA */
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci#ifndef CONFIG_CYZ_INTR
2828c2ecf20Sopenharmony_cistatic void cyz_poll(struct timer_list *);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci/* The Cyclades-Z polling cycle is defined by this variable */
2858c2ecf20Sopenharmony_cistatic long cyz_polling_cycle = CZ_DEF_POLL;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_cistatic DEFINE_TIMER(cyz_timerlist, cyz_poll);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci#else				/* CONFIG_CYZ_INTR */
2908c2ecf20Sopenharmony_cistatic void cyz_rx_restart(struct timer_list *);
2918c2ecf20Sopenharmony_ci#endif				/* CONFIG_CYZ_INTR */
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic void cyy_writeb(struct cyclades_port *port, u32 reg, u8 val)
2948c2ecf20Sopenharmony_ci{
2958c2ecf20Sopenharmony_ci	struct cyclades_card *card = port->card;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	cy_writeb(port->u.cyy.base_addr + (reg << card->bus_index), val);
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cistatic u8 cyy_readb(struct cyclades_port *port, u32 reg)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	struct cyclades_card *card = port->card;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	return readb(port->u.cyy.base_addr + (reg << card->bus_index));
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_cistatic inline bool cy_is_Z(struct cyclades_card *card)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	return card->num_chips == (unsigned int)-1;
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistatic inline bool __cyz_fpga_loaded(struct RUNTIME_9060 __iomem *ctl_addr)
3138c2ecf20Sopenharmony_ci{
3148c2ecf20Sopenharmony_ci	return readl(&ctl_addr->init_ctrl) & (1 << 17);
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_cistatic inline bool cyz_fpga_loaded(struct cyclades_card *card)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	return __cyz_fpga_loaded(card->ctl_addr.p9060);
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_cistatic bool cyz_is_loaded(struct cyclades_card *card)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	struct FIRM_ID __iomem *fw_id = card->base_addr + ID_ADDRESS;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	return (card->hw_ver == ZO_V1 || cyz_fpga_loaded(card)) &&
3278c2ecf20Sopenharmony_ci			readl(&fw_id->signature) == ZFIRM_ID;
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cistatic int serial_paranoia_check(struct cyclades_port *info,
3318c2ecf20Sopenharmony_ci		const char *name, const char *routine)
3328c2ecf20Sopenharmony_ci{
3338c2ecf20Sopenharmony_ci#ifdef SERIAL_PARANOIA_CHECK
3348c2ecf20Sopenharmony_ci	if (!info) {
3358c2ecf20Sopenharmony_ci		printk(KERN_WARNING "cyc Warning: null cyclades_port for (%s) "
3368c2ecf20Sopenharmony_ci				"in %s\n", name, routine);
3378c2ecf20Sopenharmony_ci		return 1;
3388c2ecf20Sopenharmony_ci	}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	if (info->magic != CYCLADES_MAGIC) {
3418c2ecf20Sopenharmony_ci		printk(KERN_WARNING "cyc Warning: bad magic number for serial "
3428c2ecf20Sopenharmony_ci				"struct (%s) in %s\n", name, routine);
3438c2ecf20Sopenharmony_ci		return 1;
3448c2ecf20Sopenharmony_ci	}
3458c2ecf20Sopenharmony_ci#endif
3468c2ecf20Sopenharmony_ci	return 0;
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci/***********************************************************/
3508c2ecf20Sopenharmony_ci/********* Start of block of Cyclom-Y specific code ********/
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci/* This routine waits up to 1000 micro-seconds for the previous
3538c2ecf20Sopenharmony_ci   command to the Cirrus chip to complete and then issues the
3548c2ecf20Sopenharmony_ci   new command.  An error is returned if the previous command
3558c2ecf20Sopenharmony_ci   didn't finish within the time limit.
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci   This function is only called from inside spinlock-protected code.
3588c2ecf20Sopenharmony_ci */
3598c2ecf20Sopenharmony_cistatic int __cyy_issue_cmd(void __iomem *base_addr, u8 cmd, int index)
3608c2ecf20Sopenharmony_ci{
3618c2ecf20Sopenharmony_ci	void __iomem *ccr = base_addr + (CyCCR << index);
3628c2ecf20Sopenharmony_ci	unsigned int i;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	/* Check to see that the previous command has completed */
3658c2ecf20Sopenharmony_ci	for (i = 0; i < 100; i++) {
3668c2ecf20Sopenharmony_ci		if (readb(ccr) == 0)
3678c2ecf20Sopenharmony_ci			break;
3688c2ecf20Sopenharmony_ci		udelay(10L);
3698c2ecf20Sopenharmony_ci	}
3708c2ecf20Sopenharmony_ci	/* if the CCR never cleared, the previous command
3718c2ecf20Sopenharmony_ci	   didn't finish within the "reasonable time" */
3728c2ecf20Sopenharmony_ci	if (i == 100)
3738c2ecf20Sopenharmony_ci		return -1;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	/* Issue the new command */
3768c2ecf20Sopenharmony_ci	cy_writeb(ccr, cmd);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	return 0;
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_cistatic inline int cyy_issue_cmd(struct cyclades_port *port, u8 cmd)
3828c2ecf20Sopenharmony_ci{
3838c2ecf20Sopenharmony_ci	return __cyy_issue_cmd(port->u.cyy.base_addr, cmd,
3848c2ecf20Sopenharmony_ci			port->card->bus_index);
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci#ifdef CONFIG_ISA
3888c2ecf20Sopenharmony_ci/* ISA interrupt detection code */
3898c2ecf20Sopenharmony_cistatic unsigned detect_isa_irq(void __iomem *address)
3908c2ecf20Sopenharmony_ci{
3918c2ecf20Sopenharmony_ci	int irq;
3928c2ecf20Sopenharmony_ci	unsigned long irqs, flags;
3938c2ecf20Sopenharmony_ci	int save_xir, save_car;
3948c2ecf20Sopenharmony_ci	int index = 0;		/* IRQ probing is only for ISA */
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	/* forget possible initially masked and pending IRQ */
3978c2ecf20Sopenharmony_ci	irq = probe_irq_off(probe_irq_on());
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	/* Clear interrupts on the board first */
4008c2ecf20Sopenharmony_ci	cy_writeb(address + (Cy_ClrIntr << index), 0);
4018c2ecf20Sopenharmony_ci	/* Cy_ClrIntr is 0x1800 */
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	irqs = probe_irq_on();
4048c2ecf20Sopenharmony_ci	/* Wait ... */
4058c2ecf20Sopenharmony_ci	msleep(5);
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	/* Enable the Tx interrupts on the CD1400 */
4088c2ecf20Sopenharmony_ci	local_irq_save(flags);
4098c2ecf20Sopenharmony_ci	cy_writeb(address + (CyCAR << index), 0);
4108c2ecf20Sopenharmony_ci	__cyy_issue_cmd(address, CyCHAN_CTL | CyENB_XMTR, index);
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	cy_writeb(address + (CyCAR << index), 0);
4138c2ecf20Sopenharmony_ci	cy_writeb(address + (CySRER << index),
4148c2ecf20Sopenharmony_ci		  readb(address + (CySRER << index)) | CyTxRdy);
4158c2ecf20Sopenharmony_ci	local_irq_restore(flags);
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	/* Wait ... */
4188c2ecf20Sopenharmony_ci	msleep(5);
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	/* Check which interrupt is in use */
4218c2ecf20Sopenharmony_ci	irq = probe_irq_off(irqs);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	/* Clean up */
4248c2ecf20Sopenharmony_ci	save_xir = (u_char) readb(address + (CyTIR << index));
4258c2ecf20Sopenharmony_ci	save_car = readb(address + (CyCAR << index));
4268c2ecf20Sopenharmony_ci	cy_writeb(address + (CyCAR << index), (save_xir & 0x3));
4278c2ecf20Sopenharmony_ci	cy_writeb(address + (CySRER << index),
4288c2ecf20Sopenharmony_ci		  readb(address + (CySRER << index)) & ~CyTxRdy);
4298c2ecf20Sopenharmony_ci	cy_writeb(address + (CyTIR << index), (save_xir & 0x3f));
4308c2ecf20Sopenharmony_ci	cy_writeb(address + (CyCAR << index), (save_car));
4318c2ecf20Sopenharmony_ci	cy_writeb(address + (Cy_ClrIntr << index), 0);
4328c2ecf20Sopenharmony_ci	/* Cy_ClrIntr is 0x1800 */
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	return (irq > 0) ? irq : 0;
4358c2ecf20Sopenharmony_ci}
4368c2ecf20Sopenharmony_ci#endif				/* CONFIG_ISA */
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_cistatic void cyy_chip_rx(struct cyclades_card *cinfo, int chip,
4398c2ecf20Sopenharmony_ci		void __iomem *base_addr)
4408c2ecf20Sopenharmony_ci{
4418c2ecf20Sopenharmony_ci	struct cyclades_port *info;
4428c2ecf20Sopenharmony_ci	struct tty_port *port;
4438c2ecf20Sopenharmony_ci	int len, index = cinfo->bus_index;
4448c2ecf20Sopenharmony_ci	u8 ivr, save_xir, channel, save_car, data, char_count;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_INTERRUPTS
4478c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "cyy_interrupt: rcvd intr, chip %d\n", chip);
4488c2ecf20Sopenharmony_ci#endif
4498c2ecf20Sopenharmony_ci	/* determine the channel & change to that context */
4508c2ecf20Sopenharmony_ci	save_xir = readb(base_addr + (CyRIR << index));
4518c2ecf20Sopenharmony_ci	channel = save_xir & CyIRChannel;
4528c2ecf20Sopenharmony_ci	info = &cinfo->ports[channel + chip * 4];
4538c2ecf20Sopenharmony_ci	port = &info->port;
4548c2ecf20Sopenharmony_ci	save_car = cyy_readb(info, CyCAR);
4558c2ecf20Sopenharmony_ci	cyy_writeb(info, CyCAR, save_xir);
4568c2ecf20Sopenharmony_ci	ivr = cyy_readb(info, CyRIVR) & CyIVRMask;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	/* there is an open port for this data */
4598c2ecf20Sopenharmony_ci	if (ivr == CyIVRRxEx) {	/* exception */
4608c2ecf20Sopenharmony_ci		data = cyy_readb(info, CyRDSR);
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci		/* For statistics only */
4638c2ecf20Sopenharmony_ci		if (data & CyBREAK)
4648c2ecf20Sopenharmony_ci			info->icount.brk++;
4658c2ecf20Sopenharmony_ci		else if (data & CyFRAME)
4668c2ecf20Sopenharmony_ci			info->icount.frame++;
4678c2ecf20Sopenharmony_ci		else if (data & CyPARITY)
4688c2ecf20Sopenharmony_ci			info->icount.parity++;
4698c2ecf20Sopenharmony_ci		else if (data & CyOVERRUN)
4708c2ecf20Sopenharmony_ci			info->icount.overrun++;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci		if (data & info->ignore_status_mask) {
4738c2ecf20Sopenharmony_ci			info->icount.rx++;
4748c2ecf20Sopenharmony_ci			return;
4758c2ecf20Sopenharmony_ci		}
4768c2ecf20Sopenharmony_ci		if (tty_buffer_request_room(port, 1)) {
4778c2ecf20Sopenharmony_ci			if (data & info->read_status_mask) {
4788c2ecf20Sopenharmony_ci				if (data & CyBREAK) {
4798c2ecf20Sopenharmony_ci					tty_insert_flip_char(port,
4808c2ecf20Sopenharmony_ci						cyy_readb(info, CyRDSR),
4818c2ecf20Sopenharmony_ci						TTY_BREAK);
4828c2ecf20Sopenharmony_ci					info->icount.rx++;
4838c2ecf20Sopenharmony_ci					if (port->flags & ASYNC_SAK) {
4848c2ecf20Sopenharmony_ci						struct tty_struct *tty =
4858c2ecf20Sopenharmony_ci							tty_port_tty_get(port);
4868c2ecf20Sopenharmony_ci						if (tty) {
4878c2ecf20Sopenharmony_ci							do_SAK(tty);
4888c2ecf20Sopenharmony_ci							tty_kref_put(tty);
4898c2ecf20Sopenharmony_ci						}
4908c2ecf20Sopenharmony_ci					}
4918c2ecf20Sopenharmony_ci				} else if (data & CyFRAME) {
4928c2ecf20Sopenharmony_ci					tty_insert_flip_char(port,
4938c2ecf20Sopenharmony_ci						cyy_readb(info, CyRDSR),
4948c2ecf20Sopenharmony_ci						TTY_FRAME);
4958c2ecf20Sopenharmony_ci					info->icount.rx++;
4968c2ecf20Sopenharmony_ci					info->idle_stats.frame_errs++;
4978c2ecf20Sopenharmony_ci				} else if (data & CyPARITY) {
4988c2ecf20Sopenharmony_ci					/* Pieces of seven... */
4998c2ecf20Sopenharmony_ci					tty_insert_flip_char(port,
5008c2ecf20Sopenharmony_ci						cyy_readb(info, CyRDSR),
5018c2ecf20Sopenharmony_ci						TTY_PARITY);
5028c2ecf20Sopenharmony_ci					info->icount.rx++;
5038c2ecf20Sopenharmony_ci					info->idle_stats.parity_errs++;
5048c2ecf20Sopenharmony_ci				} else if (data & CyOVERRUN) {
5058c2ecf20Sopenharmony_ci					tty_insert_flip_char(port, 0,
5068c2ecf20Sopenharmony_ci							TTY_OVERRUN);
5078c2ecf20Sopenharmony_ci					info->icount.rx++;
5088c2ecf20Sopenharmony_ci					/* If the flip buffer itself is
5098c2ecf20Sopenharmony_ci					   overflowing, we still lose
5108c2ecf20Sopenharmony_ci					   the next incoming character.
5118c2ecf20Sopenharmony_ci					 */
5128c2ecf20Sopenharmony_ci					tty_insert_flip_char(port,
5138c2ecf20Sopenharmony_ci						cyy_readb(info, CyRDSR),
5148c2ecf20Sopenharmony_ci						TTY_FRAME);
5158c2ecf20Sopenharmony_ci					info->icount.rx++;
5168c2ecf20Sopenharmony_ci					info->idle_stats.overruns++;
5178c2ecf20Sopenharmony_ci				/* These two conditions may imply */
5188c2ecf20Sopenharmony_ci				/* a normal read should be done. */
5198c2ecf20Sopenharmony_ci				/* } else if(data & CyTIMEOUT) { */
5208c2ecf20Sopenharmony_ci				/* } else if(data & CySPECHAR) { */
5218c2ecf20Sopenharmony_ci				} else {
5228c2ecf20Sopenharmony_ci					tty_insert_flip_char(port, 0,
5238c2ecf20Sopenharmony_ci							TTY_NORMAL);
5248c2ecf20Sopenharmony_ci					info->icount.rx++;
5258c2ecf20Sopenharmony_ci				}
5268c2ecf20Sopenharmony_ci			} else {
5278c2ecf20Sopenharmony_ci				tty_insert_flip_char(port, 0, TTY_NORMAL);
5288c2ecf20Sopenharmony_ci				info->icount.rx++;
5298c2ecf20Sopenharmony_ci			}
5308c2ecf20Sopenharmony_ci		} else {
5318c2ecf20Sopenharmony_ci			/* there was a software buffer overrun and nothing
5328c2ecf20Sopenharmony_ci			 * could be done about it!!! */
5338c2ecf20Sopenharmony_ci			info->icount.buf_overrun++;
5348c2ecf20Sopenharmony_ci			info->idle_stats.overruns++;
5358c2ecf20Sopenharmony_ci		}
5368c2ecf20Sopenharmony_ci	} else {	/* normal character reception */
5378c2ecf20Sopenharmony_ci		/* load # chars available from the chip */
5388c2ecf20Sopenharmony_ci		char_count = cyy_readb(info, CyRDCR);
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci#ifdef CY_ENABLE_MONITORING
5418c2ecf20Sopenharmony_ci		++info->mon.int_count;
5428c2ecf20Sopenharmony_ci		info->mon.char_count += char_count;
5438c2ecf20Sopenharmony_ci		if (char_count > info->mon.char_max)
5448c2ecf20Sopenharmony_ci			info->mon.char_max = char_count;
5458c2ecf20Sopenharmony_ci		info->mon.char_last = char_count;
5468c2ecf20Sopenharmony_ci#endif
5478c2ecf20Sopenharmony_ci		len = tty_buffer_request_room(port, char_count);
5488c2ecf20Sopenharmony_ci		while (len--) {
5498c2ecf20Sopenharmony_ci			data = cyy_readb(info, CyRDSR);
5508c2ecf20Sopenharmony_ci			tty_insert_flip_char(port, data, TTY_NORMAL);
5518c2ecf20Sopenharmony_ci			info->idle_stats.recv_bytes++;
5528c2ecf20Sopenharmony_ci			info->icount.rx++;
5538c2ecf20Sopenharmony_ci#ifdef CY_16Y_HACK
5548c2ecf20Sopenharmony_ci			udelay(10L);
5558c2ecf20Sopenharmony_ci#endif
5568c2ecf20Sopenharmony_ci		}
5578c2ecf20Sopenharmony_ci		info->idle_stats.recv_idle = jiffies;
5588c2ecf20Sopenharmony_ci	}
5598c2ecf20Sopenharmony_ci	tty_flip_buffer_push(port);
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	/* end of service */
5628c2ecf20Sopenharmony_ci	cyy_writeb(info, CyRIR, save_xir & 0x3f);
5638c2ecf20Sopenharmony_ci	cyy_writeb(info, CyCAR, save_car);
5648c2ecf20Sopenharmony_ci}
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_cistatic void cyy_chip_tx(struct cyclades_card *cinfo, unsigned int chip,
5678c2ecf20Sopenharmony_ci		void __iomem *base_addr)
5688c2ecf20Sopenharmony_ci{
5698c2ecf20Sopenharmony_ci	struct cyclades_port *info;
5708c2ecf20Sopenharmony_ci	struct tty_struct *tty;
5718c2ecf20Sopenharmony_ci	int char_count, index = cinfo->bus_index;
5728c2ecf20Sopenharmony_ci	u8 save_xir, channel, save_car, outch;
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	/* Since we only get here when the transmit buffer
5758c2ecf20Sopenharmony_ci	   is empty, we know we can always stuff a dozen
5768c2ecf20Sopenharmony_ci	   characters. */
5778c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_INTERRUPTS
5788c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "cyy_interrupt: xmit intr, chip %d\n", chip);
5798c2ecf20Sopenharmony_ci#endif
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	/* determine the channel & change to that context */
5828c2ecf20Sopenharmony_ci	save_xir = readb(base_addr + (CyTIR << index));
5838c2ecf20Sopenharmony_ci	channel = save_xir & CyIRChannel;
5848c2ecf20Sopenharmony_ci	save_car = readb(base_addr + (CyCAR << index));
5858c2ecf20Sopenharmony_ci	cy_writeb(base_addr + (CyCAR << index), save_xir);
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	info = &cinfo->ports[channel + chip * 4];
5888c2ecf20Sopenharmony_ci	tty = tty_port_tty_get(&info->port);
5898c2ecf20Sopenharmony_ci	if (tty == NULL) {
5908c2ecf20Sopenharmony_ci		cyy_writeb(info, CySRER, cyy_readb(info, CySRER) & ~CyTxRdy);
5918c2ecf20Sopenharmony_ci		goto end;
5928c2ecf20Sopenharmony_ci	}
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	/* load the on-chip space for outbound data */
5958c2ecf20Sopenharmony_ci	char_count = info->xmit_fifo_size;
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	if (info->x_char) {	/* send special char */
5988c2ecf20Sopenharmony_ci		outch = info->x_char;
5998c2ecf20Sopenharmony_ci		cyy_writeb(info, CyTDR, outch);
6008c2ecf20Sopenharmony_ci		char_count--;
6018c2ecf20Sopenharmony_ci		info->icount.tx++;
6028c2ecf20Sopenharmony_ci		info->x_char = 0;
6038c2ecf20Sopenharmony_ci	}
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	if (info->breakon || info->breakoff) {
6068c2ecf20Sopenharmony_ci		if (info->breakon) {
6078c2ecf20Sopenharmony_ci			cyy_writeb(info, CyTDR, 0);
6088c2ecf20Sopenharmony_ci			cyy_writeb(info, CyTDR, 0x81);
6098c2ecf20Sopenharmony_ci			info->breakon = 0;
6108c2ecf20Sopenharmony_ci			char_count -= 2;
6118c2ecf20Sopenharmony_ci		}
6128c2ecf20Sopenharmony_ci		if (info->breakoff) {
6138c2ecf20Sopenharmony_ci			cyy_writeb(info, CyTDR, 0);
6148c2ecf20Sopenharmony_ci			cyy_writeb(info, CyTDR, 0x83);
6158c2ecf20Sopenharmony_ci			info->breakoff = 0;
6168c2ecf20Sopenharmony_ci			char_count -= 2;
6178c2ecf20Sopenharmony_ci		}
6188c2ecf20Sopenharmony_ci	}
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	while (char_count-- > 0) {
6218c2ecf20Sopenharmony_ci		if (!info->xmit_cnt) {
6228c2ecf20Sopenharmony_ci			if (cyy_readb(info, CySRER) & CyTxMpty) {
6238c2ecf20Sopenharmony_ci				cyy_writeb(info, CySRER,
6248c2ecf20Sopenharmony_ci					cyy_readb(info, CySRER) & ~CyTxMpty);
6258c2ecf20Sopenharmony_ci			} else {
6268c2ecf20Sopenharmony_ci				cyy_writeb(info, CySRER, CyTxMpty |
6278c2ecf20Sopenharmony_ci					(cyy_readb(info, CySRER) & ~CyTxRdy));
6288c2ecf20Sopenharmony_ci			}
6298c2ecf20Sopenharmony_ci			goto done;
6308c2ecf20Sopenharmony_ci		}
6318c2ecf20Sopenharmony_ci		if (info->port.xmit_buf == NULL) {
6328c2ecf20Sopenharmony_ci			cyy_writeb(info, CySRER,
6338c2ecf20Sopenharmony_ci				cyy_readb(info, CySRER) & ~CyTxRdy);
6348c2ecf20Sopenharmony_ci			goto done;
6358c2ecf20Sopenharmony_ci		}
6368c2ecf20Sopenharmony_ci		if (tty->stopped || tty->hw_stopped) {
6378c2ecf20Sopenharmony_ci			cyy_writeb(info, CySRER,
6388c2ecf20Sopenharmony_ci				cyy_readb(info, CySRER) & ~CyTxRdy);
6398c2ecf20Sopenharmony_ci			goto done;
6408c2ecf20Sopenharmony_ci		}
6418c2ecf20Sopenharmony_ci		/* Because the Embedded Transmit Commands have been enabled,
6428c2ecf20Sopenharmony_ci		 * we must check to see if the escape character, NULL, is being
6438c2ecf20Sopenharmony_ci		 * sent. If it is, we must ensure that there is room for it to
6448c2ecf20Sopenharmony_ci		 * be doubled in the output stream.  Therefore we no longer
6458c2ecf20Sopenharmony_ci		 * advance the pointer when the character is fetched, but
6468c2ecf20Sopenharmony_ci		 * rather wait until after the check for a NULL output
6478c2ecf20Sopenharmony_ci		 * character. This is necessary because there may not be room
6488c2ecf20Sopenharmony_ci		 * for the two chars needed to send a NULL.)
6498c2ecf20Sopenharmony_ci		 */
6508c2ecf20Sopenharmony_ci		outch = info->port.xmit_buf[info->xmit_tail];
6518c2ecf20Sopenharmony_ci		if (outch) {
6528c2ecf20Sopenharmony_ci			info->xmit_cnt--;
6538c2ecf20Sopenharmony_ci			info->xmit_tail = (info->xmit_tail + 1) &
6548c2ecf20Sopenharmony_ci					(SERIAL_XMIT_SIZE - 1);
6558c2ecf20Sopenharmony_ci			cyy_writeb(info, CyTDR, outch);
6568c2ecf20Sopenharmony_ci			info->icount.tx++;
6578c2ecf20Sopenharmony_ci		} else {
6588c2ecf20Sopenharmony_ci			if (char_count > 1) {
6598c2ecf20Sopenharmony_ci				info->xmit_cnt--;
6608c2ecf20Sopenharmony_ci				info->xmit_tail = (info->xmit_tail + 1) &
6618c2ecf20Sopenharmony_ci					(SERIAL_XMIT_SIZE - 1);
6628c2ecf20Sopenharmony_ci				cyy_writeb(info, CyTDR, outch);
6638c2ecf20Sopenharmony_ci				cyy_writeb(info, CyTDR, 0);
6648c2ecf20Sopenharmony_ci				info->icount.tx++;
6658c2ecf20Sopenharmony_ci				char_count--;
6668c2ecf20Sopenharmony_ci			}
6678c2ecf20Sopenharmony_ci		}
6688c2ecf20Sopenharmony_ci	}
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_cidone:
6718c2ecf20Sopenharmony_ci	tty_wakeup(tty);
6728c2ecf20Sopenharmony_ci	tty_kref_put(tty);
6738c2ecf20Sopenharmony_ciend:
6748c2ecf20Sopenharmony_ci	/* end of service */
6758c2ecf20Sopenharmony_ci	cyy_writeb(info, CyTIR, save_xir & 0x3f);
6768c2ecf20Sopenharmony_ci	cyy_writeb(info, CyCAR, save_car);
6778c2ecf20Sopenharmony_ci}
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_cistatic void cyy_chip_modem(struct cyclades_card *cinfo, int chip,
6808c2ecf20Sopenharmony_ci		void __iomem *base_addr)
6818c2ecf20Sopenharmony_ci{
6828c2ecf20Sopenharmony_ci	struct cyclades_port *info;
6838c2ecf20Sopenharmony_ci	struct tty_struct *tty;
6848c2ecf20Sopenharmony_ci	int index = cinfo->bus_index;
6858c2ecf20Sopenharmony_ci	u8 save_xir, channel, save_car, mdm_change, mdm_status;
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	/* determine the channel & change to that context */
6888c2ecf20Sopenharmony_ci	save_xir = readb(base_addr + (CyMIR << index));
6898c2ecf20Sopenharmony_ci	channel = save_xir & CyIRChannel;
6908c2ecf20Sopenharmony_ci	info = &cinfo->ports[channel + chip * 4];
6918c2ecf20Sopenharmony_ci	save_car = cyy_readb(info, CyCAR);
6928c2ecf20Sopenharmony_ci	cyy_writeb(info, CyCAR, save_xir);
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci	mdm_change = cyy_readb(info, CyMISR);
6958c2ecf20Sopenharmony_ci	mdm_status = cyy_readb(info, CyMSVR1);
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	tty = tty_port_tty_get(&info->port);
6988c2ecf20Sopenharmony_ci	if (!tty)
6998c2ecf20Sopenharmony_ci		goto end;
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	if (mdm_change & CyANY_DELTA) {
7028c2ecf20Sopenharmony_ci		/* For statistics only */
7038c2ecf20Sopenharmony_ci		if (mdm_change & CyDCD)
7048c2ecf20Sopenharmony_ci			info->icount.dcd++;
7058c2ecf20Sopenharmony_ci		if (mdm_change & CyCTS)
7068c2ecf20Sopenharmony_ci			info->icount.cts++;
7078c2ecf20Sopenharmony_ci		if (mdm_change & CyDSR)
7088c2ecf20Sopenharmony_ci			info->icount.dsr++;
7098c2ecf20Sopenharmony_ci		if (mdm_change & CyRI)
7108c2ecf20Sopenharmony_ci			info->icount.rng++;
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci		wake_up_interruptible(&info->port.delta_msr_wait);
7138c2ecf20Sopenharmony_ci	}
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	if ((mdm_change & CyDCD) && tty_port_check_carrier(&info->port)) {
7168c2ecf20Sopenharmony_ci		if (mdm_status & CyDCD)
7178c2ecf20Sopenharmony_ci			wake_up_interruptible(&info->port.open_wait);
7188c2ecf20Sopenharmony_ci		else
7198c2ecf20Sopenharmony_ci			tty_hangup(tty);
7208c2ecf20Sopenharmony_ci	}
7218c2ecf20Sopenharmony_ci	if ((mdm_change & CyCTS) && tty_port_cts_enabled(&info->port)) {
7228c2ecf20Sopenharmony_ci		if (tty->hw_stopped) {
7238c2ecf20Sopenharmony_ci			if (mdm_status & CyCTS) {
7248c2ecf20Sopenharmony_ci				/* cy_start isn't used
7258c2ecf20Sopenharmony_ci				   because... !!! */
7268c2ecf20Sopenharmony_ci				tty->hw_stopped = 0;
7278c2ecf20Sopenharmony_ci				cyy_writeb(info, CySRER,
7288c2ecf20Sopenharmony_ci					cyy_readb(info, CySRER) | CyTxRdy);
7298c2ecf20Sopenharmony_ci				tty_wakeup(tty);
7308c2ecf20Sopenharmony_ci			}
7318c2ecf20Sopenharmony_ci		} else {
7328c2ecf20Sopenharmony_ci			if (!(mdm_status & CyCTS)) {
7338c2ecf20Sopenharmony_ci				/* cy_stop isn't used
7348c2ecf20Sopenharmony_ci				   because ... !!! */
7358c2ecf20Sopenharmony_ci				tty->hw_stopped = 1;
7368c2ecf20Sopenharmony_ci				cyy_writeb(info, CySRER,
7378c2ecf20Sopenharmony_ci					cyy_readb(info, CySRER) & ~CyTxRdy);
7388c2ecf20Sopenharmony_ci			}
7398c2ecf20Sopenharmony_ci		}
7408c2ecf20Sopenharmony_ci	}
7418c2ecf20Sopenharmony_ci/*	if (mdm_change & CyDSR) {
7428c2ecf20Sopenharmony_ci	}
7438c2ecf20Sopenharmony_ci	if (mdm_change & CyRI) {
7448c2ecf20Sopenharmony_ci	}*/
7458c2ecf20Sopenharmony_ci	tty_kref_put(tty);
7468c2ecf20Sopenharmony_ciend:
7478c2ecf20Sopenharmony_ci	/* end of service */
7488c2ecf20Sopenharmony_ci	cyy_writeb(info, CyMIR, save_xir & 0x3f);
7498c2ecf20Sopenharmony_ci	cyy_writeb(info, CyCAR, save_car);
7508c2ecf20Sopenharmony_ci}
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci/* The real interrupt service routine is called
7538c2ecf20Sopenharmony_ci   whenever the card wants its hand held--chars
7548c2ecf20Sopenharmony_ci   received, out buffer empty, modem change, etc.
7558c2ecf20Sopenharmony_ci */
7568c2ecf20Sopenharmony_cistatic irqreturn_t cyy_interrupt(int irq, void *dev_id)
7578c2ecf20Sopenharmony_ci{
7588c2ecf20Sopenharmony_ci	int status;
7598c2ecf20Sopenharmony_ci	struct cyclades_card *cinfo = dev_id;
7608c2ecf20Sopenharmony_ci	void __iomem *base_addr, *card_base_addr;
7618c2ecf20Sopenharmony_ci	unsigned int chip, too_many, had_work;
7628c2ecf20Sopenharmony_ci	int index;
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	if (unlikely(cinfo == NULL)) {
7658c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_INTERRUPTS
7668c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "cyy_interrupt: spurious interrupt %d\n",
7678c2ecf20Sopenharmony_ci				irq);
7688c2ecf20Sopenharmony_ci#endif
7698c2ecf20Sopenharmony_ci		return IRQ_NONE;	/* spurious interrupt */
7708c2ecf20Sopenharmony_ci	}
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	card_base_addr = cinfo->base_addr;
7738c2ecf20Sopenharmony_ci	index = cinfo->bus_index;
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	/* card was not initialized yet (e.g. DEBUG_SHIRQ) */
7768c2ecf20Sopenharmony_ci	if (unlikely(card_base_addr == NULL))
7778c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	/* This loop checks all chips in the card.  Make a note whenever
7808c2ecf20Sopenharmony_ci	   _any_ chip had some work to do, as this is considered an
7818c2ecf20Sopenharmony_ci	   indication that there will be more to do.  Only when no chip
7828c2ecf20Sopenharmony_ci	   has any work does this outermost loop exit.
7838c2ecf20Sopenharmony_ci	 */
7848c2ecf20Sopenharmony_ci	do {
7858c2ecf20Sopenharmony_ci		had_work = 0;
7868c2ecf20Sopenharmony_ci		for (chip = 0; chip < cinfo->num_chips; chip++) {
7878c2ecf20Sopenharmony_ci			base_addr = cinfo->base_addr +
7888c2ecf20Sopenharmony_ci					(cy_chip_offset[chip] << index);
7898c2ecf20Sopenharmony_ci			too_many = 0;
7908c2ecf20Sopenharmony_ci			while ((status = readb(base_addr +
7918c2ecf20Sopenharmony_ci						(CySVRR << index))) != 0x00) {
7928c2ecf20Sopenharmony_ci				had_work++;
7938c2ecf20Sopenharmony_ci			/* The purpose of the following test is to ensure that
7948c2ecf20Sopenharmony_ci			   no chip can monopolize the driver.  This forces the
7958c2ecf20Sopenharmony_ci			   chips to be checked in a round-robin fashion (after
7968c2ecf20Sopenharmony_ci			   draining each of a bunch (1000) of characters).
7978c2ecf20Sopenharmony_ci			 */
7988c2ecf20Sopenharmony_ci				if (1000 < too_many++)
7998c2ecf20Sopenharmony_ci					break;
8008c2ecf20Sopenharmony_ci				spin_lock(&cinfo->card_lock);
8018c2ecf20Sopenharmony_ci				if (status & CySRReceive) /* rx intr */
8028c2ecf20Sopenharmony_ci					cyy_chip_rx(cinfo, chip, base_addr);
8038c2ecf20Sopenharmony_ci				if (status & CySRTransmit) /* tx intr */
8048c2ecf20Sopenharmony_ci					cyy_chip_tx(cinfo, chip, base_addr);
8058c2ecf20Sopenharmony_ci				if (status & CySRModem) /* modem intr */
8068c2ecf20Sopenharmony_ci					cyy_chip_modem(cinfo, chip, base_addr);
8078c2ecf20Sopenharmony_ci				spin_unlock(&cinfo->card_lock);
8088c2ecf20Sopenharmony_ci			}
8098c2ecf20Sopenharmony_ci		}
8108c2ecf20Sopenharmony_ci	} while (had_work);
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	/* clear interrupts */
8138c2ecf20Sopenharmony_ci	spin_lock(&cinfo->card_lock);
8148c2ecf20Sopenharmony_ci	cy_writeb(card_base_addr + (Cy_ClrIntr << index), 0);
8158c2ecf20Sopenharmony_ci	/* Cy_ClrIntr is 0x1800 */
8168c2ecf20Sopenharmony_ci	spin_unlock(&cinfo->card_lock);
8178c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
8188c2ecf20Sopenharmony_ci}				/* cyy_interrupt */
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_cistatic void cyy_change_rts_dtr(struct cyclades_port *info, unsigned int set,
8218c2ecf20Sopenharmony_ci		unsigned int clear)
8228c2ecf20Sopenharmony_ci{
8238c2ecf20Sopenharmony_ci	struct cyclades_card *card = info->card;
8248c2ecf20Sopenharmony_ci	int channel = info->line - card->first_line;
8258c2ecf20Sopenharmony_ci	u32 rts, dtr, msvrr, msvrd;
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	channel &= 0x03;
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	if (info->rtsdtr_inv) {
8308c2ecf20Sopenharmony_ci		msvrr = CyMSVR2;
8318c2ecf20Sopenharmony_ci		msvrd = CyMSVR1;
8328c2ecf20Sopenharmony_ci		rts = CyDTR;
8338c2ecf20Sopenharmony_ci		dtr = CyRTS;
8348c2ecf20Sopenharmony_ci	} else {
8358c2ecf20Sopenharmony_ci		msvrr = CyMSVR1;
8368c2ecf20Sopenharmony_ci		msvrd = CyMSVR2;
8378c2ecf20Sopenharmony_ci		rts = CyRTS;
8388c2ecf20Sopenharmony_ci		dtr = CyDTR;
8398c2ecf20Sopenharmony_ci	}
8408c2ecf20Sopenharmony_ci	if (set & TIOCM_RTS) {
8418c2ecf20Sopenharmony_ci		cyy_writeb(info, CyCAR, channel);
8428c2ecf20Sopenharmony_ci		cyy_writeb(info, msvrr, rts);
8438c2ecf20Sopenharmony_ci	}
8448c2ecf20Sopenharmony_ci	if (clear & TIOCM_RTS) {
8458c2ecf20Sopenharmony_ci		cyy_writeb(info, CyCAR, channel);
8468c2ecf20Sopenharmony_ci		cyy_writeb(info, msvrr, ~rts);
8478c2ecf20Sopenharmony_ci	}
8488c2ecf20Sopenharmony_ci	if (set & TIOCM_DTR) {
8498c2ecf20Sopenharmony_ci		cyy_writeb(info, CyCAR, channel);
8508c2ecf20Sopenharmony_ci		cyy_writeb(info, msvrd, dtr);
8518c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_DTR
8528c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "cyc:set_modem_info raising DTR\n");
8538c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "     status: 0x%x, 0x%x\n",
8548c2ecf20Sopenharmony_ci			cyy_readb(info, CyMSVR1),
8558c2ecf20Sopenharmony_ci			cyy_readb(info, CyMSVR2));
8568c2ecf20Sopenharmony_ci#endif
8578c2ecf20Sopenharmony_ci	}
8588c2ecf20Sopenharmony_ci	if (clear & TIOCM_DTR) {
8598c2ecf20Sopenharmony_ci		cyy_writeb(info, CyCAR, channel);
8608c2ecf20Sopenharmony_ci		cyy_writeb(info, msvrd, ~dtr);
8618c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_DTR
8628c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "cyc:set_modem_info dropping DTR\n");
8638c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "     status: 0x%x, 0x%x\n",
8648c2ecf20Sopenharmony_ci			cyy_readb(info, CyMSVR1),
8658c2ecf20Sopenharmony_ci			cyy_readb(info, CyMSVR2));
8668c2ecf20Sopenharmony_ci#endif
8678c2ecf20Sopenharmony_ci	}
8688c2ecf20Sopenharmony_ci}
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci/***********************************************************/
8718c2ecf20Sopenharmony_ci/********* End of block of Cyclom-Y specific code **********/
8728c2ecf20Sopenharmony_ci/******** Start of block of Cyclades-Z specific code *******/
8738c2ecf20Sopenharmony_ci/***********************************************************/
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_cistatic int
8768c2ecf20Sopenharmony_cicyz_fetch_msg(struct cyclades_card *cinfo,
8778c2ecf20Sopenharmony_ci		__u32 *channel, __u8 *cmd, __u32 *param)
8788c2ecf20Sopenharmony_ci{
8798c2ecf20Sopenharmony_ci	struct BOARD_CTRL __iomem *board_ctrl = cinfo->board_ctrl;
8808c2ecf20Sopenharmony_ci	unsigned long loc_doorbell;
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	loc_doorbell = readl(&cinfo->ctl_addr.p9060->loc_doorbell);
8838c2ecf20Sopenharmony_ci	if (loc_doorbell) {
8848c2ecf20Sopenharmony_ci		*cmd = (char)(0xff & loc_doorbell);
8858c2ecf20Sopenharmony_ci		*channel = readl(&board_ctrl->fwcmd_channel);
8868c2ecf20Sopenharmony_ci		*param = (__u32) readl(&board_ctrl->fwcmd_param);
8878c2ecf20Sopenharmony_ci		cy_writel(&cinfo->ctl_addr.p9060->loc_doorbell, 0xffffffff);
8888c2ecf20Sopenharmony_ci		return 1;
8898c2ecf20Sopenharmony_ci	}
8908c2ecf20Sopenharmony_ci	return 0;
8918c2ecf20Sopenharmony_ci}				/* cyz_fetch_msg */
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_cistatic int
8948c2ecf20Sopenharmony_cicyz_issue_cmd(struct cyclades_card *cinfo,
8958c2ecf20Sopenharmony_ci		__u32 channel, __u8 cmd, __u32 param)
8968c2ecf20Sopenharmony_ci{
8978c2ecf20Sopenharmony_ci	struct BOARD_CTRL __iomem *board_ctrl = cinfo->board_ctrl;
8988c2ecf20Sopenharmony_ci	__u32 __iomem *pci_doorbell;
8998c2ecf20Sopenharmony_ci	unsigned int index;
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	if (!cyz_is_loaded(cinfo))
9028c2ecf20Sopenharmony_ci		return -1;
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	index = 0;
9058c2ecf20Sopenharmony_ci	pci_doorbell = &cinfo->ctl_addr.p9060->pci_doorbell;
9068c2ecf20Sopenharmony_ci	while ((readl(pci_doorbell) & 0xff) != 0) {
9078c2ecf20Sopenharmony_ci		if (index++ == 1000)
9088c2ecf20Sopenharmony_ci			return (int)(readl(pci_doorbell) & 0xff);
9098c2ecf20Sopenharmony_ci		udelay(50L);
9108c2ecf20Sopenharmony_ci	}
9118c2ecf20Sopenharmony_ci	cy_writel(&board_ctrl->hcmd_channel, channel);
9128c2ecf20Sopenharmony_ci	cy_writel(&board_ctrl->hcmd_param, param);
9138c2ecf20Sopenharmony_ci	cy_writel(pci_doorbell, (long)cmd);
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci	return 0;
9168c2ecf20Sopenharmony_ci}				/* cyz_issue_cmd */
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_cistatic void cyz_handle_rx(struct cyclades_port *info)
9198c2ecf20Sopenharmony_ci{
9208c2ecf20Sopenharmony_ci	struct BUF_CTRL __iomem *buf_ctrl = info->u.cyz.buf_ctrl;
9218c2ecf20Sopenharmony_ci	struct cyclades_card *cinfo = info->card;
9228c2ecf20Sopenharmony_ci	struct tty_port *port = &info->port;
9238c2ecf20Sopenharmony_ci	unsigned int char_count;
9248c2ecf20Sopenharmony_ci	int len;
9258c2ecf20Sopenharmony_ci#ifdef BLOCKMOVE
9268c2ecf20Sopenharmony_ci	unsigned char *buf;
9278c2ecf20Sopenharmony_ci#else
9288c2ecf20Sopenharmony_ci	char data;
9298c2ecf20Sopenharmony_ci#endif
9308c2ecf20Sopenharmony_ci	__u32 rx_put, rx_get, new_rx_get, rx_bufsize, rx_bufaddr;
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci	rx_get = new_rx_get = readl(&buf_ctrl->rx_get);
9338c2ecf20Sopenharmony_ci	rx_put = readl(&buf_ctrl->rx_put);
9348c2ecf20Sopenharmony_ci	rx_bufsize = readl(&buf_ctrl->rx_bufsize);
9358c2ecf20Sopenharmony_ci	rx_bufaddr = readl(&buf_ctrl->rx_bufaddr);
9368c2ecf20Sopenharmony_ci	if (rx_put >= rx_get)
9378c2ecf20Sopenharmony_ci		char_count = rx_put - rx_get;
9388c2ecf20Sopenharmony_ci	else
9398c2ecf20Sopenharmony_ci		char_count = rx_put - rx_get + rx_bufsize;
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci	if (!char_count)
9428c2ecf20Sopenharmony_ci		return;
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci#ifdef CY_ENABLE_MONITORING
9458c2ecf20Sopenharmony_ci	info->mon.int_count++;
9468c2ecf20Sopenharmony_ci	info->mon.char_count += char_count;
9478c2ecf20Sopenharmony_ci	if (char_count > info->mon.char_max)
9488c2ecf20Sopenharmony_ci		info->mon.char_max = char_count;
9498c2ecf20Sopenharmony_ci	info->mon.char_last = char_count;
9508c2ecf20Sopenharmony_ci#endif
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci#ifdef BLOCKMOVE
9538c2ecf20Sopenharmony_ci	/* we'd like to use memcpy(t, f, n) and memset(s, c, count)
9548c2ecf20Sopenharmony_ci	   for performance, but because of buffer boundaries, there
9558c2ecf20Sopenharmony_ci	   may be several steps to the operation */
9568c2ecf20Sopenharmony_ci	while (1) {
9578c2ecf20Sopenharmony_ci		len = tty_prepare_flip_string(port, &buf,
9588c2ecf20Sopenharmony_ci				char_count);
9598c2ecf20Sopenharmony_ci		if (!len)
9608c2ecf20Sopenharmony_ci			break;
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci		len = min_t(unsigned int, min(len, char_count),
9638c2ecf20Sopenharmony_ci				rx_bufsize - new_rx_get);
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci		memcpy_fromio(buf, cinfo->base_addr +
9668c2ecf20Sopenharmony_ci				rx_bufaddr + new_rx_get, len);
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci		new_rx_get = (new_rx_get + len) &
9698c2ecf20Sopenharmony_ci				(rx_bufsize - 1);
9708c2ecf20Sopenharmony_ci		char_count -= len;
9718c2ecf20Sopenharmony_ci		info->icount.rx += len;
9728c2ecf20Sopenharmony_ci		info->idle_stats.recv_bytes += len;
9738c2ecf20Sopenharmony_ci	}
9748c2ecf20Sopenharmony_ci#else
9758c2ecf20Sopenharmony_ci	len = tty_buffer_request_room(port, char_count);
9768c2ecf20Sopenharmony_ci	while (len--) {
9778c2ecf20Sopenharmony_ci		data = readb(cinfo->base_addr + rx_bufaddr +
9788c2ecf20Sopenharmony_ci				new_rx_get);
9798c2ecf20Sopenharmony_ci		new_rx_get = (new_rx_get + 1) &
9808c2ecf20Sopenharmony_ci					(rx_bufsize - 1);
9818c2ecf20Sopenharmony_ci		tty_insert_flip_char(port, data, TTY_NORMAL);
9828c2ecf20Sopenharmony_ci		info->idle_stats.recv_bytes++;
9838c2ecf20Sopenharmony_ci		info->icount.rx++;
9848c2ecf20Sopenharmony_ci	}
9858c2ecf20Sopenharmony_ci#endif
9868c2ecf20Sopenharmony_ci#ifdef CONFIG_CYZ_INTR
9878c2ecf20Sopenharmony_ci	/* Recalculate the number of chars in the RX buffer and issue
9888c2ecf20Sopenharmony_ci	   a cmd in case it's higher than the RX high water mark */
9898c2ecf20Sopenharmony_ci	rx_put = readl(&buf_ctrl->rx_put);
9908c2ecf20Sopenharmony_ci	if (rx_put >= rx_get)
9918c2ecf20Sopenharmony_ci		char_count = rx_put - rx_get;
9928c2ecf20Sopenharmony_ci	else
9938c2ecf20Sopenharmony_ci		char_count = rx_put - rx_get + rx_bufsize;
9948c2ecf20Sopenharmony_ci	if (char_count >= readl(&buf_ctrl->rx_threshold) &&
9958c2ecf20Sopenharmony_ci			!timer_pending(&info->rx_full_timer))
9968c2ecf20Sopenharmony_ci		mod_timer(&info->rx_full_timer, jiffies + 1);
9978c2ecf20Sopenharmony_ci#endif
9988c2ecf20Sopenharmony_ci	info->idle_stats.recv_idle = jiffies;
9998c2ecf20Sopenharmony_ci	tty_flip_buffer_push(&info->port);
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	/* Update rx_get */
10028c2ecf20Sopenharmony_ci	cy_writel(&buf_ctrl->rx_get, new_rx_get);
10038c2ecf20Sopenharmony_ci}
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_cistatic void cyz_handle_tx(struct cyclades_port *info)
10068c2ecf20Sopenharmony_ci{
10078c2ecf20Sopenharmony_ci	struct BUF_CTRL __iomem *buf_ctrl = info->u.cyz.buf_ctrl;
10088c2ecf20Sopenharmony_ci	struct cyclades_card *cinfo = info->card;
10098c2ecf20Sopenharmony_ci	struct tty_struct *tty;
10108c2ecf20Sopenharmony_ci	u8 data;
10118c2ecf20Sopenharmony_ci	unsigned int char_count;
10128c2ecf20Sopenharmony_ci#ifdef BLOCKMOVE
10138c2ecf20Sopenharmony_ci	int small_count;
10148c2ecf20Sopenharmony_ci#endif
10158c2ecf20Sopenharmony_ci	__u32 tx_put, tx_get, tx_bufsize, tx_bufaddr;
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	if (info->xmit_cnt <= 0)	/* Nothing to transmit */
10188c2ecf20Sopenharmony_ci		return;
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci	tx_get = readl(&buf_ctrl->tx_get);
10218c2ecf20Sopenharmony_ci	tx_put = readl(&buf_ctrl->tx_put);
10228c2ecf20Sopenharmony_ci	tx_bufsize = readl(&buf_ctrl->tx_bufsize);
10238c2ecf20Sopenharmony_ci	tx_bufaddr = readl(&buf_ctrl->tx_bufaddr);
10248c2ecf20Sopenharmony_ci	if (tx_put >= tx_get)
10258c2ecf20Sopenharmony_ci		char_count = tx_get - tx_put - 1 + tx_bufsize;
10268c2ecf20Sopenharmony_ci	else
10278c2ecf20Sopenharmony_ci		char_count = tx_get - tx_put - 1;
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_ci	if (!char_count)
10308c2ecf20Sopenharmony_ci		return;
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_ci	tty = tty_port_tty_get(&info->port);
10338c2ecf20Sopenharmony_ci	if (tty == NULL)
10348c2ecf20Sopenharmony_ci		goto ztxdone;
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci	if (info->x_char) {	/* send special char */
10378c2ecf20Sopenharmony_ci		data = info->x_char;
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci		cy_writeb(cinfo->base_addr + tx_bufaddr + tx_put, data);
10408c2ecf20Sopenharmony_ci		tx_put = (tx_put + 1) & (tx_bufsize - 1);
10418c2ecf20Sopenharmony_ci		info->x_char = 0;
10428c2ecf20Sopenharmony_ci		char_count--;
10438c2ecf20Sopenharmony_ci		info->icount.tx++;
10448c2ecf20Sopenharmony_ci	}
10458c2ecf20Sopenharmony_ci#ifdef BLOCKMOVE
10468c2ecf20Sopenharmony_ci	while (0 < (small_count = min_t(unsigned int,
10478c2ecf20Sopenharmony_ci			tx_bufsize - tx_put, min_t(unsigned int,
10488c2ecf20Sopenharmony_ci				(SERIAL_XMIT_SIZE - info->xmit_tail),
10498c2ecf20Sopenharmony_ci				min_t(unsigned int, info->xmit_cnt,
10508c2ecf20Sopenharmony_ci					char_count))))) {
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_ci		memcpy_toio((char *)(cinfo->base_addr + tx_bufaddr + tx_put),
10538c2ecf20Sopenharmony_ci				&info->port.xmit_buf[info->xmit_tail],
10548c2ecf20Sopenharmony_ci				small_count);
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci		tx_put = (tx_put + small_count) & (tx_bufsize - 1);
10578c2ecf20Sopenharmony_ci		char_count -= small_count;
10588c2ecf20Sopenharmony_ci		info->icount.tx += small_count;
10598c2ecf20Sopenharmony_ci		info->xmit_cnt -= small_count;
10608c2ecf20Sopenharmony_ci		info->xmit_tail = (info->xmit_tail + small_count) &
10618c2ecf20Sopenharmony_ci				(SERIAL_XMIT_SIZE - 1);
10628c2ecf20Sopenharmony_ci	}
10638c2ecf20Sopenharmony_ci#else
10648c2ecf20Sopenharmony_ci	while (info->xmit_cnt && char_count) {
10658c2ecf20Sopenharmony_ci		data = info->port.xmit_buf[info->xmit_tail];
10668c2ecf20Sopenharmony_ci		info->xmit_cnt--;
10678c2ecf20Sopenharmony_ci		info->xmit_tail = (info->xmit_tail + 1) &
10688c2ecf20Sopenharmony_ci				(SERIAL_XMIT_SIZE - 1);
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci		cy_writeb(cinfo->base_addr + tx_bufaddr + tx_put, data);
10718c2ecf20Sopenharmony_ci		tx_put = (tx_put + 1) & (tx_bufsize - 1);
10728c2ecf20Sopenharmony_ci		char_count--;
10738c2ecf20Sopenharmony_ci		info->icount.tx++;
10748c2ecf20Sopenharmony_ci	}
10758c2ecf20Sopenharmony_ci#endif
10768c2ecf20Sopenharmony_ci	tty_wakeup(tty);
10778c2ecf20Sopenharmony_ci	tty_kref_put(tty);
10788c2ecf20Sopenharmony_ciztxdone:
10798c2ecf20Sopenharmony_ci	/* Update tx_put */
10808c2ecf20Sopenharmony_ci	cy_writel(&buf_ctrl->tx_put, tx_put);
10818c2ecf20Sopenharmony_ci}
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_cistatic void cyz_handle_cmd(struct cyclades_card *cinfo)
10848c2ecf20Sopenharmony_ci{
10858c2ecf20Sopenharmony_ci	struct BOARD_CTRL __iomem *board_ctrl = cinfo->board_ctrl;
10868c2ecf20Sopenharmony_ci	struct cyclades_port *info;
10878c2ecf20Sopenharmony_ci	__u32 channel, param, fw_ver;
10888c2ecf20Sopenharmony_ci	__u8 cmd;
10898c2ecf20Sopenharmony_ci	int special_count;
10908c2ecf20Sopenharmony_ci	int delta_count;
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci	fw_ver = readl(&board_ctrl->fw_version);
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ci	while (cyz_fetch_msg(cinfo, &channel, &cmd, &param) == 1) {
10958c2ecf20Sopenharmony_ci		special_count = 0;
10968c2ecf20Sopenharmony_ci		delta_count = 0;
10978c2ecf20Sopenharmony_ci		info = &cinfo->ports[channel];
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_ci		switch (cmd) {
11008c2ecf20Sopenharmony_ci		case C_CM_PR_ERROR:
11018c2ecf20Sopenharmony_ci			tty_insert_flip_char(&info->port, 0, TTY_PARITY);
11028c2ecf20Sopenharmony_ci			info->icount.rx++;
11038c2ecf20Sopenharmony_ci			special_count++;
11048c2ecf20Sopenharmony_ci			break;
11058c2ecf20Sopenharmony_ci		case C_CM_FR_ERROR:
11068c2ecf20Sopenharmony_ci			tty_insert_flip_char(&info->port, 0, TTY_FRAME);
11078c2ecf20Sopenharmony_ci			info->icount.rx++;
11088c2ecf20Sopenharmony_ci			special_count++;
11098c2ecf20Sopenharmony_ci			break;
11108c2ecf20Sopenharmony_ci		case C_CM_RXBRK:
11118c2ecf20Sopenharmony_ci			tty_insert_flip_char(&info->port, 0, TTY_BREAK);
11128c2ecf20Sopenharmony_ci			info->icount.rx++;
11138c2ecf20Sopenharmony_ci			special_count++;
11148c2ecf20Sopenharmony_ci			break;
11158c2ecf20Sopenharmony_ci		case C_CM_MDCD:
11168c2ecf20Sopenharmony_ci			info->icount.dcd++;
11178c2ecf20Sopenharmony_ci			delta_count++;
11188c2ecf20Sopenharmony_ci			if (tty_port_check_carrier(&info->port)) {
11198c2ecf20Sopenharmony_ci				u32 dcd = fw_ver > 241 ? param :
11208c2ecf20Sopenharmony_ci					readl(&info->u.cyz.ch_ctrl->rs_status);
11218c2ecf20Sopenharmony_ci				if (dcd & C_RS_DCD)
11228c2ecf20Sopenharmony_ci					wake_up_interruptible(&info->port.open_wait);
11238c2ecf20Sopenharmony_ci				else
11248c2ecf20Sopenharmony_ci					tty_port_tty_hangup(&info->port, false);
11258c2ecf20Sopenharmony_ci			}
11268c2ecf20Sopenharmony_ci			break;
11278c2ecf20Sopenharmony_ci		case C_CM_MCTS:
11288c2ecf20Sopenharmony_ci			info->icount.cts++;
11298c2ecf20Sopenharmony_ci			delta_count++;
11308c2ecf20Sopenharmony_ci			break;
11318c2ecf20Sopenharmony_ci		case C_CM_MRI:
11328c2ecf20Sopenharmony_ci			info->icount.rng++;
11338c2ecf20Sopenharmony_ci			delta_count++;
11348c2ecf20Sopenharmony_ci			break;
11358c2ecf20Sopenharmony_ci		case C_CM_MDSR:
11368c2ecf20Sopenharmony_ci			info->icount.dsr++;
11378c2ecf20Sopenharmony_ci			delta_count++;
11388c2ecf20Sopenharmony_ci			break;
11398c2ecf20Sopenharmony_ci#ifdef Z_WAKE
11408c2ecf20Sopenharmony_ci		case C_CM_IOCTLW:
11418c2ecf20Sopenharmony_ci			complete(&info->shutdown_wait);
11428c2ecf20Sopenharmony_ci			break;
11438c2ecf20Sopenharmony_ci#endif
11448c2ecf20Sopenharmony_ci#ifdef CONFIG_CYZ_INTR
11458c2ecf20Sopenharmony_ci		case C_CM_RXHIWM:
11468c2ecf20Sopenharmony_ci		case C_CM_RXNNDT:
11478c2ecf20Sopenharmony_ci		case C_CM_INTBACK2:
11488c2ecf20Sopenharmony_ci			/* Reception Interrupt */
11498c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_INTERRUPTS
11508c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "cyz_interrupt: rcvd intr, card %d, "
11518c2ecf20Sopenharmony_ci					"port %ld\n", info->card, channel);
11528c2ecf20Sopenharmony_ci#endif
11538c2ecf20Sopenharmony_ci			cyz_handle_rx(info);
11548c2ecf20Sopenharmony_ci			break;
11558c2ecf20Sopenharmony_ci		case C_CM_TXBEMPTY:
11568c2ecf20Sopenharmony_ci		case C_CM_TXLOWWM:
11578c2ecf20Sopenharmony_ci		case C_CM_INTBACK:
11588c2ecf20Sopenharmony_ci			/* Transmission Interrupt */
11598c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_INTERRUPTS
11608c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "cyz_interrupt: xmit intr, card %d, "
11618c2ecf20Sopenharmony_ci					"port %ld\n", info->card, channel);
11628c2ecf20Sopenharmony_ci#endif
11638c2ecf20Sopenharmony_ci			cyz_handle_tx(info);
11648c2ecf20Sopenharmony_ci			break;
11658c2ecf20Sopenharmony_ci#endif				/* CONFIG_CYZ_INTR */
11668c2ecf20Sopenharmony_ci		case C_CM_FATAL:
11678c2ecf20Sopenharmony_ci			/* should do something with this !!! */
11688c2ecf20Sopenharmony_ci			break;
11698c2ecf20Sopenharmony_ci		default:
11708c2ecf20Sopenharmony_ci			break;
11718c2ecf20Sopenharmony_ci		}
11728c2ecf20Sopenharmony_ci		if (delta_count)
11738c2ecf20Sopenharmony_ci			wake_up_interruptible(&info->port.delta_msr_wait);
11748c2ecf20Sopenharmony_ci		if (special_count)
11758c2ecf20Sopenharmony_ci			tty_flip_buffer_push(&info->port);
11768c2ecf20Sopenharmony_ci	}
11778c2ecf20Sopenharmony_ci}
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_ci#ifdef CONFIG_CYZ_INTR
11808c2ecf20Sopenharmony_cistatic irqreturn_t cyz_interrupt(int irq, void *dev_id)
11818c2ecf20Sopenharmony_ci{
11828c2ecf20Sopenharmony_ci	struct cyclades_card *cinfo = dev_id;
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci	if (unlikely(!cyz_is_loaded(cinfo))) {
11858c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_INTERRUPTS
11868c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "cyz_interrupt: board not yet loaded "
11878c2ecf20Sopenharmony_ci				"(IRQ%d).\n", irq);
11888c2ecf20Sopenharmony_ci#endif
11898c2ecf20Sopenharmony_ci		return IRQ_NONE;
11908c2ecf20Sopenharmony_ci	}
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_ci	/* Handle the interrupts */
11938c2ecf20Sopenharmony_ci	cyz_handle_cmd(cinfo);
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
11968c2ecf20Sopenharmony_ci}				/* cyz_interrupt */
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_cistatic void cyz_rx_restart(struct timer_list *t)
11998c2ecf20Sopenharmony_ci{
12008c2ecf20Sopenharmony_ci	struct cyclades_port *info = from_timer(info, t, rx_full_timer);
12018c2ecf20Sopenharmony_ci	struct cyclades_card *card = info->card;
12028c2ecf20Sopenharmony_ci	int retval;
12038c2ecf20Sopenharmony_ci	__u32 channel = info->line - card->first_line;
12048c2ecf20Sopenharmony_ci	unsigned long flags;
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci	spin_lock_irqsave(&card->card_lock, flags);
12078c2ecf20Sopenharmony_ci	retval = cyz_issue_cmd(card, channel, C_CM_INTBACK2, 0L);
12088c2ecf20Sopenharmony_ci	if (retval != 0) {
12098c2ecf20Sopenharmony_ci		printk(KERN_ERR "cyc:cyz_rx_restart retval on ttyC%d was %x\n",
12108c2ecf20Sopenharmony_ci			info->line, retval);
12118c2ecf20Sopenharmony_ci	}
12128c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&card->card_lock, flags);
12138c2ecf20Sopenharmony_ci}
12148c2ecf20Sopenharmony_ci
12158c2ecf20Sopenharmony_ci#else				/* CONFIG_CYZ_INTR */
12168c2ecf20Sopenharmony_ci
12178c2ecf20Sopenharmony_cistatic void cyz_poll(struct timer_list *unused)
12188c2ecf20Sopenharmony_ci{
12198c2ecf20Sopenharmony_ci	struct cyclades_card *cinfo;
12208c2ecf20Sopenharmony_ci	struct cyclades_port *info;
12218c2ecf20Sopenharmony_ci	unsigned long expires = jiffies + HZ;
12228c2ecf20Sopenharmony_ci	unsigned int port, card;
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci	for (card = 0; card < NR_CARDS; card++) {
12258c2ecf20Sopenharmony_ci		cinfo = &cy_card[card];
12268c2ecf20Sopenharmony_ci
12278c2ecf20Sopenharmony_ci		if (!cy_is_Z(cinfo))
12288c2ecf20Sopenharmony_ci			continue;
12298c2ecf20Sopenharmony_ci		if (!cyz_is_loaded(cinfo))
12308c2ecf20Sopenharmony_ci			continue;
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci	/* Skip first polling cycle to avoid racing conditions with the FW */
12338c2ecf20Sopenharmony_ci		if (!cinfo->intr_enabled) {
12348c2ecf20Sopenharmony_ci			cinfo->intr_enabled = 1;
12358c2ecf20Sopenharmony_ci			continue;
12368c2ecf20Sopenharmony_ci		}
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci		cyz_handle_cmd(cinfo);
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_ci		for (port = 0; port < cinfo->nports; port++) {
12418c2ecf20Sopenharmony_ci			info = &cinfo->ports[port];
12428c2ecf20Sopenharmony_ci
12438c2ecf20Sopenharmony_ci			if (!info->throttle)
12448c2ecf20Sopenharmony_ci				cyz_handle_rx(info);
12458c2ecf20Sopenharmony_ci			cyz_handle_tx(info);
12468c2ecf20Sopenharmony_ci		}
12478c2ecf20Sopenharmony_ci		/* poll every 'cyz_polling_cycle' period */
12488c2ecf20Sopenharmony_ci		expires = jiffies + cyz_polling_cycle;
12498c2ecf20Sopenharmony_ci	}
12508c2ecf20Sopenharmony_ci	mod_timer(&cyz_timerlist, expires);
12518c2ecf20Sopenharmony_ci}				/* cyz_poll */
12528c2ecf20Sopenharmony_ci
12538c2ecf20Sopenharmony_ci#endif				/* CONFIG_CYZ_INTR */
12548c2ecf20Sopenharmony_ci
12558c2ecf20Sopenharmony_ci/********** End of block of Cyclades-Z specific code *********/
12568c2ecf20Sopenharmony_ci/***********************************************************/
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_ci/* This is called whenever a port becomes active;
12598c2ecf20Sopenharmony_ci   interrupts are enabled and DTR & RTS are turned on.
12608c2ecf20Sopenharmony_ci */
12618c2ecf20Sopenharmony_cistatic int cy_startup(struct cyclades_port *info, struct tty_struct *tty)
12628c2ecf20Sopenharmony_ci{
12638c2ecf20Sopenharmony_ci	struct cyclades_card *card;
12648c2ecf20Sopenharmony_ci	unsigned long flags;
12658c2ecf20Sopenharmony_ci	int retval = 0;
12668c2ecf20Sopenharmony_ci	int channel;
12678c2ecf20Sopenharmony_ci	unsigned long page;
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_ci	card = info->card;
12708c2ecf20Sopenharmony_ci	channel = info->line - card->first_line;
12718c2ecf20Sopenharmony_ci
12728c2ecf20Sopenharmony_ci	page = get_zeroed_page(GFP_KERNEL);
12738c2ecf20Sopenharmony_ci	if (!page)
12748c2ecf20Sopenharmony_ci		return -ENOMEM;
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_ci	spin_lock_irqsave(&card->card_lock, flags);
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_ci	if (tty_port_initialized(&info->port))
12798c2ecf20Sopenharmony_ci		goto errout;
12808c2ecf20Sopenharmony_ci
12818c2ecf20Sopenharmony_ci	if (!info->type) {
12828c2ecf20Sopenharmony_ci		set_bit(TTY_IO_ERROR, &tty->flags);
12838c2ecf20Sopenharmony_ci		goto errout;
12848c2ecf20Sopenharmony_ci	}
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci	if (info->port.xmit_buf)
12878c2ecf20Sopenharmony_ci		free_page(page);
12888c2ecf20Sopenharmony_ci	else
12898c2ecf20Sopenharmony_ci		info->port.xmit_buf = (unsigned char *)page;
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&card->card_lock, flags);
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_ci	cy_set_line_char(info, tty);
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ci	if (!cy_is_Z(card)) {
12968c2ecf20Sopenharmony_ci		channel &= 0x03;
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_ci		spin_lock_irqsave(&card->card_lock, flags);
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_ci		cyy_writeb(info, CyCAR, channel);
13018c2ecf20Sopenharmony_ci
13028c2ecf20Sopenharmony_ci		cyy_writeb(info, CyRTPR,
13038c2ecf20Sopenharmony_ci			(info->default_timeout ? info->default_timeout : 0x02));
13048c2ecf20Sopenharmony_ci		/* 10ms rx timeout */
13058c2ecf20Sopenharmony_ci
13068c2ecf20Sopenharmony_ci		cyy_issue_cmd(info, CyCHAN_CTL | CyENB_RCVR | CyENB_XMTR);
13078c2ecf20Sopenharmony_ci
13088c2ecf20Sopenharmony_ci		cyy_change_rts_dtr(info, TIOCM_RTS | TIOCM_DTR, 0);
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_ci		cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyRxData);
13118c2ecf20Sopenharmony_ci	} else {
13128c2ecf20Sopenharmony_ci		struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl;
13138c2ecf20Sopenharmony_ci
13148c2ecf20Sopenharmony_ci		if (!cyz_is_loaded(card))
13158c2ecf20Sopenharmony_ci			return -ENODEV;
13168c2ecf20Sopenharmony_ci
13178c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_OPEN
13188c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "cyc startup Z card %d, channel %d, "
13198c2ecf20Sopenharmony_ci			"base_addr %p\n", card, channel, card->base_addr);
13208c2ecf20Sopenharmony_ci#endif
13218c2ecf20Sopenharmony_ci		spin_lock_irqsave(&card->card_lock, flags);
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci		cy_writel(&ch_ctrl->op_mode, C_CH_ENABLE);
13248c2ecf20Sopenharmony_ci#ifdef Z_WAKE
13258c2ecf20Sopenharmony_ci#ifdef CONFIG_CYZ_INTR
13268c2ecf20Sopenharmony_ci		cy_writel(&ch_ctrl->intr_enable,
13278c2ecf20Sopenharmony_ci			  C_IN_TXBEMPTY | C_IN_TXLOWWM | C_IN_RXHIWM |
13288c2ecf20Sopenharmony_ci			  C_IN_RXNNDT | C_IN_IOCTLW | C_IN_MDCD);
13298c2ecf20Sopenharmony_ci#else
13308c2ecf20Sopenharmony_ci		cy_writel(&ch_ctrl->intr_enable,
13318c2ecf20Sopenharmony_ci			  C_IN_IOCTLW | C_IN_MDCD);
13328c2ecf20Sopenharmony_ci#endif				/* CONFIG_CYZ_INTR */
13338c2ecf20Sopenharmony_ci#else
13348c2ecf20Sopenharmony_ci#ifdef CONFIG_CYZ_INTR
13358c2ecf20Sopenharmony_ci		cy_writel(&ch_ctrl->intr_enable,
13368c2ecf20Sopenharmony_ci			  C_IN_TXBEMPTY | C_IN_TXLOWWM | C_IN_RXHIWM |
13378c2ecf20Sopenharmony_ci			  C_IN_RXNNDT | C_IN_MDCD);
13388c2ecf20Sopenharmony_ci#else
13398c2ecf20Sopenharmony_ci		cy_writel(&ch_ctrl->intr_enable, C_IN_MDCD);
13408c2ecf20Sopenharmony_ci#endif				/* CONFIG_CYZ_INTR */
13418c2ecf20Sopenharmony_ci#endif				/* Z_WAKE */
13428c2ecf20Sopenharmony_ci
13438c2ecf20Sopenharmony_ci		retval = cyz_issue_cmd(card, channel, C_CM_IOCTL, 0L);
13448c2ecf20Sopenharmony_ci		if (retval != 0) {
13458c2ecf20Sopenharmony_ci			printk(KERN_ERR "cyc:startup(1) retval on ttyC%d was "
13468c2ecf20Sopenharmony_ci				"%x\n", info->line, retval);
13478c2ecf20Sopenharmony_ci		}
13488c2ecf20Sopenharmony_ci
13498c2ecf20Sopenharmony_ci		/* Flush RX buffers before raising DTR and RTS */
13508c2ecf20Sopenharmony_ci		retval = cyz_issue_cmd(card, channel, C_CM_FLUSH_RX, 0L);
13518c2ecf20Sopenharmony_ci		if (retval != 0) {
13528c2ecf20Sopenharmony_ci			printk(KERN_ERR "cyc:startup(2) retval on ttyC%d was "
13538c2ecf20Sopenharmony_ci				"%x\n", info->line, retval);
13548c2ecf20Sopenharmony_ci		}
13558c2ecf20Sopenharmony_ci
13568c2ecf20Sopenharmony_ci		/* set timeout !!! */
13578c2ecf20Sopenharmony_ci		/* set RTS and DTR !!! */
13588c2ecf20Sopenharmony_ci		tty_port_raise_dtr_rts(&info->port);
13598c2ecf20Sopenharmony_ci
13608c2ecf20Sopenharmony_ci		/* enable send, recv, modem !!! */
13618c2ecf20Sopenharmony_ci	}
13628c2ecf20Sopenharmony_ci
13638c2ecf20Sopenharmony_ci	tty_port_set_initialized(&info->port, 1);
13648c2ecf20Sopenharmony_ci
13658c2ecf20Sopenharmony_ci	clear_bit(TTY_IO_ERROR, &tty->flags);
13668c2ecf20Sopenharmony_ci	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
13678c2ecf20Sopenharmony_ci	info->breakon = info->breakoff = 0;
13688c2ecf20Sopenharmony_ci	memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats));
13698c2ecf20Sopenharmony_ci	info->idle_stats.in_use =
13708c2ecf20Sopenharmony_ci	info->idle_stats.recv_idle =
13718c2ecf20Sopenharmony_ci	info->idle_stats.xmit_idle = jiffies;
13728c2ecf20Sopenharmony_ci
13738c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&card->card_lock, flags);
13748c2ecf20Sopenharmony_ci
13758c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_OPEN
13768c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "cyc startup done\n");
13778c2ecf20Sopenharmony_ci#endif
13788c2ecf20Sopenharmony_ci	return 0;
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_cierrout:
13818c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&card->card_lock, flags);
13828c2ecf20Sopenharmony_ci	free_page(page);
13838c2ecf20Sopenharmony_ci	return retval;
13848c2ecf20Sopenharmony_ci}				/* startup */
13858c2ecf20Sopenharmony_ci
13868c2ecf20Sopenharmony_cistatic void start_xmit(struct cyclades_port *info)
13878c2ecf20Sopenharmony_ci{
13888c2ecf20Sopenharmony_ci	struct cyclades_card *card = info->card;
13898c2ecf20Sopenharmony_ci	unsigned long flags;
13908c2ecf20Sopenharmony_ci	int channel = info->line - card->first_line;
13918c2ecf20Sopenharmony_ci
13928c2ecf20Sopenharmony_ci	if (!cy_is_Z(card)) {
13938c2ecf20Sopenharmony_ci		spin_lock_irqsave(&card->card_lock, flags);
13948c2ecf20Sopenharmony_ci		cyy_writeb(info, CyCAR, channel & 0x03);
13958c2ecf20Sopenharmony_ci		cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyTxRdy);
13968c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&card->card_lock, flags);
13978c2ecf20Sopenharmony_ci	} else {
13988c2ecf20Sopenharmony_ci#ifdef CONFIG_CYZ_INTR
13998c2ecf20Sopenharmony_ci		int retval;
14008c2ecf20Sopenharmony_ci
14018c2ecf20Sopenharmony_ci		spin_lock_irqsave(&card->card_lock, flags);
14028c2ecf20Sopenharmony_ci		retval = cyz_issue_cmd(card, channel, C_CM_INTBACK, 0L);
14038c2ecf20Sopenharmony_ci		if (retval != 0) {
14048c2ecf20Sopenharmony_ci			printk(KERN_ERR "cyc:start_xmit retval on ttyC%d was "
14058c2ecf20Sopenharmony_ci				"%x\n", info->line, retval);
14068c2ecf20Sopenharmony_ci		}
14078c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&card->card_lock, flags);
14088c2ecf20Sopenharmony_ci#else				/* CONFIG_CYZ_INTR */
14098c2ecf20Sopenharmony_ci		/* Don't have to do anything at this time */
14108c2ecf20Sopenharmony_ci#endif				/* CONFIG_CYZ_INTR */
14118c2ecf20Sopenharmony_ci	}
14128c2ecf20Sopenharmony_ci}				/* start_xmit */
14138c2ecf20Sopenharmony_ci
14148c2ecf20Sopenharmony_ci/*
14158c2ecf20Sopenharmony_ci * This routine shuts down a serial port; interrupts are disabled,
14168c2ecf20Sopenharmony_ci * and DTR is dropped if the hangup on close termio flag is on.
14178c2ecf20Sopenharmony_ci */
14188c2ecf20Sopenharmony_cistatic void cy_shutdown(struct cyclades_port *info, struct tty_struct *tty)
14198c2ecf20Sopenharmony_ci{
14208c2ecf20Sopenharmony_ci	struct cyclades_card *card;
14218c2ecf20Sopenharmony_ci	unsigned long flags;
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_ci	if (!tty_port_initialized(&info->port))
14248c2ecf20Sopenharmony_ci		return;
14258c2ecf20Sopenharmony_ci
14268c2ecf20Sopenharmony_ci	card = info->card;
14278c2ecf20Sopenharmony_ci	if (!cy_is_Z(card)) {
14288c2ecf20Sopenharmony_ci		spin_lock_irqsave(&card->card_lock, flags);
14298c2ecf20Sopenharmony_ci
14308c2ecf20Sopenharmony_ci		/* Clear delta_msr_wait queue to avoid mem leaks. */
14318c2ecf20Sopenharmony_ci		wake_up_interruptible(&info->port.delta_msr_wait);
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_ci		if (info->port.xmit_buf) {
14348c2ecf20Sopenharmony_ci			unsigned char *temp;
14358c2ecf20Sopenharmony_ci			temp = info->port.xmit_buf;
14368c2ecf20Sopenharmony_ci			info->port.xmit_buf = NULL;
14378c2ecf20Sopenharmony_ci			free_page((unsigned long)temp);
14388c2ecf20Sopenharmony_ci		}
14398c2ecf20Sopenharmony_ci		if (C_HUPCL(tty))
14408c2ecf20Sopenharmony_ci			cyy_change_rts_dtr(info, 0, TIOCM_RTS | TIOCM_DTR);
14418c2ecf20Sopenharmony_ci
14428c2ecf20Sopenharmony_ci		cyy_issue_cmd(info, CyCHAN_CTL | CyDIS_RCVR);
14438c2ecf20Sopenharmony_ci		/* it may be appropriate to clear _XMIT at
14448c2ecf20Sopenharmony_ci		   some later date (after testing)!!! */
14458c2ecf20Sopenharmony_ci
14468c2ecf20Sopenharmony_ci		set_bit(TTY_IO_ERROR, &tty->flags);
14478c2ecf20Sopenharmony_ci		tty_port_set_initialized(&info->port, 0);
14488c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&card->card_lock, flags);
14498c2ecf20Sopenharmony_ci	} else {
14508c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_OPEN
14518c2ecf20Sopenharmony_ci		int channel = info->line - card->first_line;
14528c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "cyc shutdown Z card %d, channel %d, "
14538c2ecf20Sopenharmony_ci			"base_addr %p\n", card, channel, card->base_addr);
14548c2ecf20Sopenharmony_ci#endif
14558c2ecf20Sopenharmony_ci
14568c2ecf20Sopenharmony_ci		if (!cyz_is_loaded(card))
14578c2ecf20Sopenharmony_ci			return;
14588c2ecf20Sopenharmony_ci
14598c2ecf20Sopenharmony_ci		spin_lock_irqsave(&card->card_lock, flags);
14608c2ecf20Sopenharmony_ci
14618c2ecf20Sopenharmony_ci		if (info->port.xmit_buf) {
14628c2ecf20Sopenharmony_ci			unsigned char *temp;
14638c2ecf20Sopenharmony_ci			temp = info->port.xmit_buf;
14648c2ecf20Sopenharmony_ci			info->port.xmit_buf = NULL;
14658c2ecf20Sopenharmony_ci			free_page((unsigned long)temp);
14668c2ecf20Sopenharmony_ci		}
14678c2ecf20Sopenharmony_ci
14688c2ecf20Sopenharmony_ci		if (C_HUPCL(tty))
14698c2ecf20Sopenharmony_ci			tty_port_lower_dtr_rts(&info->port);
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_ci		set_bit(TTY_IO_ERROR, &tty->flags);
14728c2ecf20Sopenharmony_ci		tty_port_set_initialized(&info->port, 0);
14738c2ecf20Sopenharmony_ci
14748c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&card->card_lock, flags);
14758c2ecf20Sopenharmony_ci	}
14768c2ecf20Sopenharmony_ci
14778c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_OPEN
14788c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "cyc shutdown done\n");
14798c2ecf20Sopenharmony_ci#endif
14808c2ecf20Sopenharmony_ci}				/* shutdown */
14818c2ecf20Sopenharmony_ci
14828c2ecf20Sopenharmony_ci/*
14838c2ecf20Sopenharmony_ci * ------------------------------------------------------------
14848c2ecf20Sopenharmony_ci * cy_open() and friends
14858c2ecf20Sopenharmony_ci * ------------------------------------------------------------
14868c2ecf20Sopenharmony_ci */
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_ci/*
14898c2ecf20Sopenharmony_ci * This routine is called whenever a serial port is opened.  It
14908c2ecf20Sopenharmony_ci * performs the serial-specific initialization for the tty structure.
14918c2ecf20Sopenharmony_ci */
14928c2ecf20Sopenharmony_cistatic int cy_open(struct tty_struct *tty, struct file *filp)
14938c2ecf20Sopenharmony_ci{
14948c2ecf20Sopenharmony_ci	struct cyclades_port *info;
14958c2ecf20Sopenharmony_ci	unsigned int i, line = tty->index;
14968c2ecf20Sopenharmony_ci	int retval;
14978c2ecf20Sopenharmony_ci
14988c2ecf20Sopenharmony_ci	for (i = 0; i < NR_CARDS; i++)
14998c2ecf20Sopenharmony_ci		if (line < cy_card[i].first_line + cy_card[i].nports &&
15008c2ecf20Sopenharmony_ci				line >= cy_card[i].first_line)
15018c2ecf20Sopenharmony_ci			break;
15028c2ecf20Sopenharmony_ci	if (i >= NR_CARDS)
15038c2ecf20Sopenharmony_ci		return -ENODEV;
15048c2ecf20Sopenharmony_ci	info = &cy_card[i].ports[line - cy_card[i].first_line];
15058c2ecf20Sopenharmony_ci	if (info->line < 0)
15068c2ecf20Sopenharmony_ci		return -ENODEV;
15078c2ecf20Sopenharmony_ci
15088c2ecf20Sopenharmony_ci	/* If the card's firmware hasn't been loaded,
15098c2ecf20Sopenharmony_ci	   treat it as absent from the system.  This
15108c2ecf20Sopenharmony_ci	   will make the user pay attention.
15118c2ecf20Sopenharmony_ci	 */
15128c2ecf20Sopenharmony_ci	if (cy_is_Z(info->card)) {
15138c2ecf20Sopenharmony_ci		struct cyclades_card *cinfo = info->card;
15148c2ecf20Sopenharmony_ci		struct FIRM_ID __iomem *firm_id = cinfo->base_addr + ID_ADDRESS;
15158c2ecf20Sopenharmony_ci
15168c2ecf20Sopenharmony_ci		if (!cyz_is_loaded(cinfo)) {
15178c2ecf20Sopenharmony_ci			if (cinfo->hw_ver == ZE_V1 && cyz_fpga_loaded(cinfo) &&
15188c2ecf20Sopenharmony_ci					readl(&firm_id->signature) ==
15198c2ecf20Sopenharmony_ci					ZFIRM_HLT) {
15208c2ecf20Sopenharmony_ci				printk(KERN_ERR "cyc:Cyclades-Z Error: you "
15218c2ecf20Sopenharmony_ci					"need an external power supply for "
15228c2ecf20Sopenharmony_ci					"this number of ports.\nFirmware "
15238c2ecf20Sopenharmony_ci					"halted.\n");
15248c2ecf20Sopenharmony_ci			} else {
15258c2ecf20Sopenharmony_ci				printk(KERN_ERR "cyc:Cyclades-Z firmware not "
15268c2ecf20Sopenharmony_ci					"yet loaded\n");
15278c2ecf20Sopenharmony_ci			}
15288c2ecf20Sopenharmony_ci			return -ENODEV;
15298c2ecf20Sopenharmony_ci		}
15308c2ecf20Sopenharmony_ci#ifdef CONFIG_CYZ_INTR
15318c2ecf20Sopenharmony_ci		else {
15328c2ecf20Sopenharmony_ci		/* In case this Z board is operating in interrupt mode, its
15338c2ecf20Sopenharmony_ci		   interrupts should be enabled as soon as the first open
15348c2ecf20Sopenharmony_ci		   happens to one of its ports. */
15358c2ecf20Sopenharmony_ci			if (!cinfo->intr_enabled) {
15368c2ecf20Sopenharmony_ci				u16 intr;
15378c2ecf20Sopenharmony_ci
15388c2ecf20Sopenharmony_ci				/* Enable interrupts on the PLX chip */
15398c2ecf20Sopenharmony_ci				intr = readw(&cinfo->ctl_addr.p9060->
15408c2ecf20Sopenharmony_ci						intr_ctrl_stat) | 0x0900;
15418c2ecf20Sopenharmony_ci				cy_writew(&cinfo->ctl_addr.p9060->
15428c2ecf20Sopenharmony_ci						intr_ctrl_stat, intr);
15438c2ecf20Sopenharmony_ci				/* Enable interrupts on the FW */
15448c2ecf20Sopenharmony_ci				retval = cyz_issue_cmd(cinfo, 0,
15458c2ecf20Sopenharmony_ci						C_CM_IRQ_ENBL, 0L);
15468c2ecf20Sopenharmony_ci				if (retval != 0) {
15478c2ecf20Sopenharmony_ci					printk(KERN_ERR "cyc:IRQ enable retval "
15488c2ecf20Sopenharmony_ci						"was %x\n", retval);
15498c2ecf20Sopenharmony_ci				}
15508c2ecf20Sopenharmony_ci				cinfo->intr_enabled = 1;
15518c2ecf20Sopenharmony_ci			}
15528c2ecf20Sopenharmony_ci		}
15538c2ecf20Sopenharmony_ci#endif				/* CONFIG_CYZ_INTR */
15548c2ecf20Sopenharmony_ci		/* Make sure this Z port really exists in hardware */
15558c2ecf20Sopenharmony_ci		if (info->line > (cinfo->first_line + cinfo->nports - 1))
15568c2ecf20Sopenharmony_ci			return -ENODEV;
15578c2ecf20Sopenharmony_ci	}
15588c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_OTHER
15598c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "cyc:cy_open ttyC%d\n", info->line);
15608c2ecf20Sopenharmony_ci#endif
15618c2ecf20Sopenharmony_ci	tty->driver_data = info;
15628c2ecf20Sopenharmony_ci	if (serial_paranoia_check(info, tty->name, "cy_open"))
15638c2ecf20Sopenharmony_ci		return -ENODEV;
15648c2ecf20Sopenharmony_ci
15658c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_OPEN
15668c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "cyc:cy_open ttyC%d, count = %d\n", info->line,
15678c2ecf20Sopenharmony_ci			info->port.count);
15688c2ecf20Sopenharmony_ci#endif
15698c2ecf20Sopenharmony_ci	info->port.count++;
15708c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_COUNT
15718c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "cyc:cy_open (%d): incrementing count to %d\n",
15728c2ecf20Sopenharmony_ci		current->pid, info->port.count);
15738c2ecf20Sopenharmony_ci#endif
15748c2ecf20Sopenharmony_ci
15758c2ecf20Sopenharmony_ci	/*
15768c2ecf20Sopenharmony_ci	 * Start up serial port
15778c2ecf20Sopenharmony_ci	 */
15788c2ecf20Sopenharmony_ci	retval = cy_startup(info, tty);
15798c2ecf20Sopenharmony_ci	if (retval)
15808c2ecf20Sopenharmony_ci		return retval;
15818c2ecf20Sopenharmony_ci
15828c2ecf20Sopenharmony_ci	retval = tty_port_block_til_ready(&info->port, tty, filp);
15838c2ecf20Sopenharmony_ci	if (retval) {
15848c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_OPEN
15858c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "cyc:cy_open returning after block_til_ready "
15868c2ecf20Sopenharmony_ci			"with %d\n", retval);
15878c2ecf20Sopenharmony_ci#endif
15888c2ecf20Sopenharmony_ci		return retval;
15898c2ecf20Sopenharmony_ci	}
15908c2ecf20Sopenharmony_ci
15918c2ecf20Sopenharmony_ci	info->throttle = 0;
15928c2ecf20Sopenharmony_ci	tty_port_tty_set(&info->port, tty);
15938c2ecf20Sopenharmony_ci
15948c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_OPEN
15958c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "cyc:cy_open done\n");
15968c2ecf20Sopenharmony_ci#endif
15978c2ecf20Sopenharmony_ci	return 0;
15988c2ecf20Sopenharmony_ci}				/* cy_open */
15998c2ecf20Sopenharmony_ci
16008c2ecf20Sopenharmony_ci/*
16018c2ecf20Sopenharmony_ci * cy_wait_until_sent() --- wait until the transmitter is empty
16028c2ecf20Sopenharmony_ci */
16038c2ecf20Sopenharmony_cistatic void cy_wait_until_sent(struct tty_struct *tty, int timeout)
16048c2ecf20Sopenharmony_ci{
16058c2ecf20Sopenharmony_ci	struct cyclades_card *card;
16068c2ecf20Sopenharmony_ci	struct cyclades_port *info = tty->driver_data;
16078c2ecf20Sopenharmony_ci	unsigned long orig_jiffies;
16088c2ecf20Sopenharmony_ci	int char_time;
16098c2ecf20Sopenharmony_ci
16108c2ecf20Sopenharmony_ci	if (serial_paranoia_check(info, tty->name, "cy_wait_until_sent"))
16118c2ecf20Sopenharmony_ci		return;
16128c2ecf20Sopenharmony_ci
16138c2ecf20Sopenharmony_ci	if (info->xmit_fifo_size == 0)
16148c2ecf20Sopenharmony_ci		return;		/* Just in case.... */
16158c2ecf20Sopenharmony_ci
16168c2ecf20Sopenharmony_ci	orig_jiffies = jiffies;
16178c2ecf20Sopenharmony_ci	/*
16188c2ecf20Sopenharmony_ci	 * Set the check interval to be 1/5 of the estimated time to
16198c2ecf20Sopenharmony_ci	 * send a single character, and make it at least 1.  The check
16208c2ecf20Sopenharmony_ci	 * interval should also be less than the timeout.
16218c2ecf20Sopenharmony_ci	 *
16228c2ecf20Sopenharmony_ci	 * Note: we have to use pretty tight timings here to satisfy
16238c2ecf20Sopenharmony_ci	 * the NIST-PCTS.
16248c2ecf20Sopenharmony_ci	 */
16258c2ecf20Sopenharmony_ci	char_time = (info->timeout - HZ / 50) / info->xmit_fifo_size;
16268c2ecf20Sopenharmony_ci	char_time = char_time / 5;
16278c2ecf20Sopenharmony_ci	if (char_time <= 0)
16288c2ecf20Sopenharmony_ci		char_time = 1;
16298c2ecf20Sopenharmony_ci	if (timeout < 0)
16308c2ecf20Sopenharmony_ci		timeout = 0;
16318c2ecf20Sopenharmony_ci	if (timeout)
16328c2ecf20Sopenharmony_ci		char_time = min(char_time, timeout);
16338c2ecf20Sopenharmony_ci	/*
16348c2ecf20Sopenharmony_ci	 * If the transmitter hasn't cleared in twice the approximate
16358c2ecf20Sopenharmony_ci	 * amount of time to send the entire FIFO, it probably won't
16368c2ecf20Sopenharmony_ci	 * ever clear.  This assumes the UART isn't doing flow
16378c2ecf20Sopenharmony_ci	 * control, which is currently the case.  Hence, if it ever
16388c2ecf20Sopenharmony_ci	 * takes longer than info->timeout, this is probably due to a
16398c2ecf20Sopenharmony_ci	 * UART bug of some kind.  So, we clamp the timeout parameter at
16408c2ecf20Sopenharmony_ci	 * 2*info->timeout.
16418c2ecf20Sopenharmony_ci	 */
16428c2ecf20Sopenharmony_ci	if (!timeout || timeout > 2 * info->timeout)
16438c2ecf20Sopenharmony_ci		timeout = 2 * info->timeout;
16448c2ecf20Sopenharmony_ci
16458c2ecf20Sopenharmony_ci	card = info->card;
16468c2ecf20Sopenharmony_ci	if (!cy_is_Z(card)) {
16478c2ecf20Sopenharmony_ci		while (cyy_readb(info, CySRER) & CyTxRdy) {
16488c2ecf20Sopenharmony_ci			if (msleep_interruptible(jiffies_to_msecs(char_time)))
16498c2ecf20Sopenharmony_ci				break;
16508c2ecf20Sopenharmony_ci			if (timeout && time_after(jiffies, orig_jiffies +
16518c2ecf20Sopenharmony_ci					timeout))
16528c2ecf20Sopenharmony_ci				break;
16538c2ecf20Sopenharmony_ci		}
16548c2ecf20Sopenharmony_ci	}
16558c2ecf20Sopenharmony_ci	/* Run one more char cycle */
16568c2ecf20Sopenharmony_ci	msleep_interruptible(jiffies_to_msecs(char_time * 5));
16578c2ecf20Sopenharmony_ci}
16588c2ecf20Sopenharmony_ci
16598c2ecf20Sopenharmony_cistatic void cy_flush_buffer(struct tty_struct *tty)
16608c2ecf20Sopenharmony_ci{
16618c2ecf20Sopenharmony_ci	struct cyclades_port *info = tty->driver_data;
16628c2ecf20Sopenharmony_ci	struct cyclades_card *card;
16638c2ecf20Sopenharmony_ci	int channel, retval;
16648c2ecf20Sopenharmony_ci	unsigned long flags;
16658c2ecf20Sopenharmony_ci
16668c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_IO
16678c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "cyc:cy_flush_buffer ttyC%d\n", info->line);
16688c2ecf20Sopenharmony_ci#endif
16698c2ecf20Sopenharmony_ci
16708c2ecf20Sopenharmony_ci	if (serial_paranoia_check(info, tty->name, "cy_flush_buffer"))
16718c2ecf20Sopenharmony_ci		return;
16728c2ecf20Sopenharmony_ci
16738c2ecf20Sopenharmony_ci	card = info->card;
16748c2ecf20Sopenharmony_ci	channel = info->line - card->first_line;
16758c2ecf20Sopenharmony_ci
16768c2ecf20Sopenharmony_ci	spin_lock_irqsave(&card->card_lock, flags);
16778c2ecf20Sopenharmony_ci	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
16788c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&card->card_lock, flags);
16798c2ecf20Sopenharmony_ci
16808c2ecf20Sopenharmony_ci	if (cy_is_Z(card)) {	/* If it is a Z card, flush the on-board
16818c2ecf20Sopenharmony_ci					   buffers as well */
16828c2ecf20Sopenharmony_ci		spin_lock_irqsave(&card->card_lock, flags);
16838c2ecf20Sopenharmony_ci		retval = cyz_issue_cmd(card, channel, C_CM_FLUSH_TX, 0L);
16848c2ecf20Sopenharmony_ci		if (retval != 0) {
16858c2ecf20Sopenharmony_ci			printk(KERN_ERR "cyc: flush_buffer retval on ttyC%d "
16868c2ecf20Sopenharmony_ci				"was %x\n", info->line, retval);
16878c2ecf20Sopenharmony_ci		}
16888c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&card->card_lock, flags);
16898c2ecf20Sopenharmony_ci	}
16908c2ecf20Sopenharmony_ci	tty_wakeup(tty);
16918c2ecf20Sopenharmony_ci}				/* cy_flush_buffer */
16928c2ecf20Sopenharmony_ci
16938c2ecf20Sopenharmony_ci
16948c2ecf20Sopenharmony_cistatic void cy_do_close(struct tty_port *port)
16958c2ecf20Sopenharmony_ci{
16968c2ecf20Sopenharmony_ci	struct cyclades_port *info = container_of(port, struct cyclades_port,
16978c2ecf20Sopenharmony_ci								port);
16988c2ecf20Sopenharmony_ci	struct cyclades_card *card;
16998c2ecf20Sopenharmony_ci	unsigned long flags;
17008c2ecf20Sopenharmony_ci	int channel;
17018c2ecf20Sopenharmony_ci
17028c2ecf20Sopenharmony_ci	card = info->card;
17038c2ecf20Sopenharmony_ci	channel = info->line - card->first_line;
17048c2ecf20Sopenharmony_ci	spin_lock_irqsave(&card->card_lock, flags);
17058c2ecf20Sopenharmony_ci
17068c2ecf20Sopenharmony_ci	if (!cy_is_Z(card)) {
17078c2ecf20Sopenharmony_ci		/* Stop accepting input */
17088c2ecf20Sopenharmony_ci		cyy_writeb(info, CyCAR, channel & 0x03);
17098c2ecf20Sopenharmony_ci		cyy_writeb(info, CySRER, cyy_readb(info, CySRER) & ~CyRxData);
17108c2ecf20Sopenharmony_ci		if (tty_port_initialized(&info->port)) {
17118c2ecf20Sopenharmony_ci			/* Waiting for on-board buffers to be empty before
17128c2ecf20Sopenharmony_ci			   closing the port */
17138c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&card->card_lock, flags);
17148c2ecf20Sopenharmony_ci			cy_wait_until_sent(port->tty, info->timeout);
17158c2ecf20Sopenharmony_ci			spin_lock_irqsave(&card->card_lock, flags);
17168c2ecf20Sopenharmony_ci		}
17178c2ecf20Sopenharmony_ci	} else {
17188c2ecf20Sopenharmony_ci#ifdef Z_WAKE
17198c2ecf20Sopenharmony_ci		/* Waiting for on-board buffers to be empty before closing
17208c2ecf20Sopenharmony_ci		   the port */
17218c2ecf20Sopenharmony_ci		struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl;
17228c2ecf20Sopenharmony_ci		int retval;
17238c2ecf20Sopenharmony_ci
17248c2ecf20Sopenharmony_ci		if (readl(&ch_ctrl->flow_status) != C_FS_TXIDLE) {
17258c2ecf20Sopenharmony_ci			retval = cyz_issue_cmd(card, channel, C_CM_IOCTLW, 0L);
17268c2ecf20Sopenharmony_ci			if (retval != 0) {
17278c2ecf20Sopenharmony_ci				printk(KERN_DEBUG "cyc:cy_close retval on "
17288c2ecf20Sopenharmony_ci					"ttyC%d was %x\n", info->line, retval);
17298c2ecf20Sopenharmony_ci			}
17308c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&card->card_lock, flags);
17318c2ecf20Sopenharmony_ci			wait_for_completion_interruptible(&info->shutdown_wait);
17328c2ecf20Sopenharmony_ci			spin_lock_irqsave(&card->card_lock, flags);
17338c2ecf20Sopenharmony_ci		}
17348c2ecf20Sopenharmony_ci#endif
17358c2ecf20Sopenharmony_ci	}
17368c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&card->card_lock, flags);
17378c2ecf20Sopenharmony_ci	cy_shutdown(info, port->tty);
17388c2ecf20Sopenharmony_ci}
17398c2ecf20Sopenharmony_ci
17408c2ecf20Sopenharmony_ci/*
17418c2ecf20Sopenharmony_ci * This routine is called when a particular tty device is closed.
17428c2ecf20Sopenharmony_ci */
17438c2ecf20Sopenharmony_cistatic void cy_close(struct tty_struct *tty, struct file *filp)
17448c2ecf20Sopenharmony_ci{
17458c2ecf20Sopenharmony_ci	struct cyclades_port *info = tty->driver_data;
17468c2ecf20Sopenharmony_ci	if (!info || serial_paranoia_check(info, tty->name, "cy_close"))
17478c2ecf20Sopenharmony_ci		return;
17488c2ecf20Sopenharmony_ci	tty_port_close(&info->port, tty, filp);
17498c2ecf20Sopenharmony_ci}				/* cy_close */
17508c2ecf20Sopenharmony_ci
17518c2ecf20Sopenharmony_ci/* This routine gets called when tty_write has put something into
17528c2ecf20Sopenharmony_ci * the write_queue.  The characters may come from user space or
17538c2ecf20Sopenharmony_ci * kernel space.
17548c2ecf20Sopenharmony_ci *
17558c2ecf20Sopenharmony_ci * This routine will return the number of characters actually
17568c2ecf20Sopenharmony_ci * accepted for writing.
17578c2ecf20Sopenharmony_ci *
17588c2ecf20Sopenharmony_ci * If the port is not already transmitting stuff, start it off by
17598c2ecf20Sopenharmony_ci * enabling interrupts.  The interrupt service routine will then
17608c2ecf20Sopenharmony_ci * ensure that the characters are sent.
17618c2ecf20Sopenharmony_ci * If the port is already active, there is no need to kick it.
17628c2ecf20Sopenharmony_ci *
17638c2ecf20Sopenharmony_ci */
17648c2ecf20Sopenharmony_cistatic int cy_write(struct tty_struct *tty, const unsigned char *buf, int count)
17658c2ecf20Sopenharmony_ci{
17668c2ecf20Sopenharmony_ci	struct cyclades_port *info = tty->driver_data;
17678c2ecf20Sopenharmony_ci	unsigned long flags;
17688c2ecf20Sopenharmony_ci	int c, ret = 0;
17698c2ecf20Sopenharmony_ci
17708c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_IO
17718c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "cyc:cy_write ttyC%d\n", info->line);
17728c2ecf20Sopenharmony_ci#endif
17738c2ecf20Sopenharmony_ci
17748c2ecf20Sopenharmony_ci	if (serial_paranoia_check(info, tty->name, "cy_write"))
17758c2ecf20Sopenharmony_ci		return 0;
17768c2ecf20Sopenharmony_ci
17778c2ecf20Sopenharmony_ci	if (!info->port.xmit_buf)
17788c2ecf20Sopenharmony_ci		return 0;
17798c2ecf20Sopenharmony_ci
17808c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->card->card_lock, flags);
17818c2ecf20Sopenharmony_ci	while (1) {
17828c2ecf20Sopenharmony_ci		c = min(count, (int)(SERIAL_XMIT_SIZE - info->xmit_cnt - 1));
17838c2ecf20Sopenharmony_ci		c = min(c, (int)(SERIAL_XMIT_SIZE - info->xmit_head));
17848c2ecf20Sopenharmony_ci
17858c2ecf20Sopenharmony_ci		if (c <= 0)
17868c2ecf20Sopenharmony_ci			break;
17878c2ecf20Sopenharmony_ci
17888c2ecf20Sopenharmony_ci		memcpy(info->port.xmit_buf + info->xmit_head, buf, c);
17898c2ecf20Sopenharmony_ci		info->xmit_head = (info->xmit_head + c) &
17908c2ecf20Sopenharmony_ci			(SERIAL_XMIT_SIZE - 1);
17918c2ecf20Sopenharmony_ci		info->xmit_cnt += c;
17928c2ecf20Sopenharmony_ci		buf += c;
17938c2ecf20Sopenharmony_ci		count -= c;
17948c2ecf20Sopenharmony_ci		ret += c;
17958c2ecf20Sopenharmony_ci	}
17968c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->card->card_lock, flags);
17978c2ecf20Sopenharmony_ci
17988c2ecf20Sopenharmony_ci	info->idle_stats.xmit_bytes += ret;
17998c2ecf20Sopenharmony_ci	info->idle_stats.xmit_idle = jiffies;
18008c2ecf20Sopenharmony_ci
18018c2ecf20Sopenharmony_ci	if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped)
18028c2ecf20Sopenharmony_ci		start_xmit(info);
18038c2ecf20Sopenharmony_ci
18048c2ecf20Sopenharmony_ci	return ret;
18058c2ecf20Sopenharmony_ci}				/* cy_write */
18068c2ecf20Sopenharmony_ci
18078c2ecf20Sopenharmony_ci/*
18088c2ecf20Sopenharmony_ci * This routine is called by the kernel to write a single
18098c2ecf20Sopenharmony_ci * character to the tty device.  If the kernel uses this routine,
18108c2ecf20Sopenharmony_ci * it must call the flush_chars() routine (if defined) when it is
18118c2ecf20Sopenharmony_ci * done stuffing characters into the driver.  If there is no room
18128c2ecf20Sopenharmony_ci * in the queue, the character is ignored.
18138c2ecf20Sopenharmony_ci */
18148c2ecf20Sopenharmony_cistatic int cy_put_char(struct tty_struct *tty, unsigned char ch)
18158c2ecf20Sopenharmony_ci{
18168c2ecf20Sopenharmony_ci	struct cyclades_port *info = tty->driver_data;
18178c2ecf20Sopenharmony_ci	unsigned long flags;
18188c2ecf20Sopenharmony_ci
18198c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_IO
18208c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "cyc:cy_put_char ttyC%d\n", info->line);
18218c2ecf20Sopenharmony_ci#endif
18228c2ecf20Sopenharmony_ci
18238c2ecf20Sopenharmony_ci	if (serial_paranoia_check(info, tty->name, "cy_put_char"))
18248c2ecf20Sopenharmony_ci		return 0;
18258c2ecf20Sopenharmony_ci
18268c2ecf20Sopenharmony_ci	if (!info->port.xmit_buf)
18278c2ecf20Sopenharmony_ci		return 0;
18288c2ecf20Sopenharmony_ci
18298c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->card->card_lock, flags);
18308c2ecf20Sopenharmony_ci	if (info->xmit_cnt >= (int)(SERIAL_XMIT_SIZE - 1)) {
18318c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&info->card->card_lock, flags);
18328c2ecf20Sopenharmony_ci		return 0;
18338c2ecf20Sopenharmony_ci	}
18348c2ecf20Sopenharmony_ci
18358c2ecf20Sopenharmony_ci	info->port.xmit_buf[info->xmit_head++] = ch;
18368c2ecf20Sopenharmony_ci	info->xmit_head &= SERIAL_XMIT_SIZE - 1;
18378c2ecf20Sopenharmony_ci	info->xmit_cnt++;
18388c2ecf20Sopenharmony_ci	info->idle_stats.xmit_bytes++;
18398c2ecf20Sopenharmony_ci	info->idle_stats.xmit_idle = jiffies;
18408c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->card->card_lock, flags);
18418c2ecf20Sopenharmony_ci	return 1;
18428c2ecf20Sopenharmony_ci}				/* cy_put_char */
18438c2ecf20Sopenharmony_ci
18448c2ecf20Sopenharmony_ci/*
18458c2ecf20Sopenharmony_ci * This routine is called by the kernel after it has written a
18468c2ecf20Sopenharmony_ci * series of characters to the tty device using put_char().
18478c2ecf20Sopenharmony_ci */
18488c2ecf20Sopenharmony_cistatic void cy_flush_chars(struct tty_struct *tty)
18498c2ecf20Sopenharmony_ci{
18508c2ecf20Sopenharmony_ci	struct cyclades_port *info = tty->driver_data;
18518c2ecf20Sopenharmony_ci
18528c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_IO
18538c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "cyc:cy_flush_chars ttyC%d\n", info->line);
18548c2ecf20Sopenharmony_ci#endif
18558c2ecf20Sopenharmony_ci
18568c2ecf20Sopenharmony_ci	if (serial_paranoia_check(info, tty->name, "cy_flush_chars"))
18578c2ecf20Sopenharmony_ci		return;
18588c2ecf20Sopenharmony_ci
18598c2ecf20Sopenharmony_ci	if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
18608c2ecf20Sopenharmony_ci			!info->port.xmit_buf)
18618c2ecf20Sopenharmony_ci		return;
18628c2ecf20Sopenharmony_ci
18638c2ecf20Sopenharmony_ci	start_xmit(info);
18648c2ecf20Sopenharmony_ci}				/* cy_flush_chars */
18658c2ecf20Sopenharmony_ci
18668c2ecf20Sopenharmony_ci/*
18678c2ecf20Sopenharmony_ci * This routine returns the numbers of characters the tty driver
18688c2ecf20Sopenharmony_ci * will accept for queuing to be written.  This number is subject
18698c2ecf20Sopenharmony_ci * to change as output buffers get emptied, or if the output flow
18708c2ecf20Sopenharmony_ci * control is activated.
18718c2ecf20Sopenharmony_ci */
18728c2ecf20Sopenharmony_cistatic int cy_write_room(struct tty_struct *tty)
18738c2ecf20Sopenharmony_ci{
18748c2ecf20Sopenharmony_ci	struct cyclades_port *info = tty->driver_data;
18758c2ecf20Sopenharmony_ci	int ret;
18768c2ecf20Sopenharmony_ci
18778c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_IO
18788c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "cyc:cy_write_room ttyC%d\n", info->line);
18798c2ecf20Sopenharmony_ci#endif
18808c2ecf20Sopenharmony_ci
18818c2ecf20Sopenharmony_ci	if (serial_paranoia_check(info, tty->name, "cy_write_room"))
18828c2ecf20Sopenharmony_ci		return 0;
18838c2ecf20Sopenharmony_ci	ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
18848c2ecf20Sopenharmony_ci	if (ret < 0)
18858c2ecf20Sopenharmony_ci		ret = 0;
18868c2ecf20Sopenharmony_ci	return ret;
18878c2ecf20Sopenharmony_ci}				/* cy_write_room */
18888c2ecf20Sopenharmony_ci
18898c2ecf20Sopenharmony_cistatic int cy_chars_in_buffer(struct tty_struct *tty)
18908c2ecf20Sopenharmony_ci{
18918c2ecf20Sopenharmony_ci	struct cyclades_port *info = tty->driver_data;
18928c2ecf20Sopenharmony_ci
18938c2ecf20Sopenharmony_ci	if (serial_paranoia_check(info, tty->name, "cy_chars_in_buffer"))
18948c2ecf20Sopenharmony_ci		return 0;
18958c2ecf20Sopenharmony_ci
18968c2ecf20Sopenharmony_ci#ifdef Z_EXT_CHARS_IN_BUFFER
18978c2ecf20Sopenharmony_ci	if (!cy_is_Z(info->card)) {
18988c2ecf20Sopenharmony_ci#endif				/* Z_EXT_CHARS_IN_BUFFER */
18998c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_IO
19008c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "cyc:cy_chars_in_buffer ttyC%d %d\n",
19018c2ecf20Sopenharmony_ci			info->line, info->xmit_cnt);
19028c2ecf20Sopenharmony_ci#endif
19038c2ecf20Sopenharmony_ci		return info->xmit_cnt;
19048c2ecf20Sopenharmony_ci#ifdef Z_EXT_CHARS_IN_BUFFER
19058c2ecf20Sopenharmony_ci	} else {
19068c2ecf20Sopenharmony_ci		struct BUF_CTRL __iomem *buf_ctrl = info->u.cyz.buf_ctrl;
19078c2ecf20Sopenharmony_ci		int char_count;
19088c2ecf20Sopenharmony_ci		__u32 tx_put, tx_get, tx_bufsize;
19098c2ecf20Sopenharmony_ci
19108c2ecf20Sopenharmony_ci		tx_get = readl(&buf_ctrl->tx_get);
19118c2ecf20Sopenharmony_ci		tx_put = readl(&buf_ctrl->tx_put);
19128c2ecf20Sopenharmony_ci		tx_bufsize = readl(&buf_ctrl->tx_bufsize);
19138c2ecf20Sopenharmony_ci		if (tx_put >= tx_get)
19148c2ecf20Sopenharmony_ci			char_count = tx_put - tx_get;
19158c2ecf20Sopenharmony_ci		else
19168c2ecf20Sopenharmony_ci			char_count = tx_put - tx_get + tx_bufsize;
19178c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_IO
19188c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "cyc:cy_chars_in_buffer ttyC%d %d\n",
19198c2ecf20Sopenharmony_ci			info->line, info->xmit_cnt + char_count);
19208c2ecf20Sopenharmony_ci#endif
19218c2ecf20Sopenharmony_ci		return info->xmit_cnt + char_count;
19228c2ecf20Sopenharmony_ci	}
19238c2ecf20Sopenharmony_ci#endif				/* Z_EXT_CHARS_IN_BUFFER */
19248c2ecf20Sopenharmony_ci}				/* cy_chars_in_buffer */
19258c2ecf20Sopenharmony_ci
19268c2ecf20Sopenharmony_ci/*
19278c2ecf20Sopenharmony_ci * ------------------------------------------------------------
19288c2ecf20Sopenharmony_ci * cy_ioctl() and friends
19298c2ecf20Sopenharmony_ci * ------------------------------------------------------------
19308c2ecf20Sopenharmony_ci */
19318c2ecf20Sopenharmony_ci
19328c2ecf20Sopenharmony_cistatic void cyy_baud_calc(struct cyclades_port *info, __u32 baud)
19338c2ecf20Sopenharmony_ci{
19348c2ecf20Sopenharmony_ci	int co, co_val, bpr;
19358c2ecf20Sopenharmony_ci	__u32 cy_clock = ((info->chip_rev >= CD1400_REV_J) ? 60000000 :
19368c2ecf20Sopenharmony_ci			25000000);
19378c2ecf20Sopenharmony_ci
19388c2ecf20Sopenharmony_ci	if (baud == 0) {
19398c2ecf20Sopenharmony_ci		info->tbpr = info->tco = info->rbpr = info->rco = 0;
19408c2ecf20Sopenharmony_ci		return;
19418c2ecf20Sopenharmony_ci	}
19428c2ecf20Sopenharmony_ci
19438c2ecf20Sopenharmony_ci	/* determine which prescaler to use */
19448c2ecf20Sopenharmony_ci	for (co = 4, co_val = 2048; co; co--, co_val >>= 2) {
19458c2ecf20Sopenharmony_ci		if (cy_clock / co_val / baud > 63)
19468c2ecf20Sopenharmony_ci			break;
19478c2ecf20Sopenharmony_ci	}
19488c2ecf20Sopenharmony_ci
19498c2ecf20Sopenharmony_ci	bpr = (cy_clock / co_val * 2 / baud + 1) / 2;
19508c2ecf20Sopenharmony_ci	if (bpr > 255)
19518c2ecf20Sopenharmony_ci		bpr = 255;
19528c2ecf20Sopenharmony_ci
19538c2ecf20Sopenharmony_ci	info->tbpr = info->rbpr = bpr;
19548c2ecf20Sopenharmony_ci	info->tco = info->rco = co;
19558c2ecf20Sopenharmony_ci}
19568c2ecf20Sopenharmony_ci
19578c2ecf20Sopenharmony_ci/*
19588c2ecf20Sopenharmony_ci * This routine finds or computes the various line characteristics.
19598c2ecf20Sopenharmony_ci * It used to be called config_setup
19608c2ecf20Sopenharmony_ci */
19618c2ecf20Sopenharmony_cistatic void cy_set_line_char(struct cyclades_port *info, struct tty_struct *tty)
19628c2ecf20Sopenharmony_ci{
19638c2ecf20Sopenharmony_ci	struct cyclades_card *card;
19648c2ecf20Sopenharmony_ci	unsigned long flags;
19658c2ecf20Sopenharmony_ci	int channel;
19668c2ecf20Sopenharmony_ci	unsigned cflag, iflag;
19678c2ecf20Sopenharmony_ci	int baud, baud_rate = 0;
19688c2ecf20Sopenharmony_ci	int i;
19698c2ecf20Sopenharmony_ci
19708c2ecf20Sopenharmony_ci	if (info->line == -1)
19718c2ecf20Sopenharmony_ci		return;
19728c2ecf20Sopenharmony_ci
19738c2ecf20Sopenharmony_ci	cflag = tty->termios.c_cflag;
19748c2ecf20Sopenharmony_ci	iflag = tty->termios.c_iflag;
19758c2ecf20Sopenharmony_ci
19768c2ecf20Sopenharmony_ci	card = info->card;
19778c2ecf20Sopenharmony_ci	channel = info->line - card->first_line;
19788c2ecf20Sopenharmony_ci
19798c2ecf20Sopenharmony_ci	if (!cy_is_Z(card)) {
19808c2ecf20Sopenharmony_ci		u32 cflags;
19818c2ecf20Sopenharmony_ci
19828c2ecf20Sopenharmony_ci		/* baud rate */
19838c2ecf20Sopenharmony_ci		baud = tty_get_baud_rate(tty);
19848c2ecf20Sopenharmony_ci		if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) ==
19858c2ecf20Sopenharmony_ci				ASYNC_SPD_CUST) {
19868c2ecf20Sopenharmony_ci			if (info->custom_divisor)
19878c2ecf20Sopenharmony_ci				baud_rate = info->baud / info->custom_divisor;
19888c2ecf20Sopenharmony_ci			else
19898c2ecf20Sopenharmony_ci				baud_rate = info->baud;
19908c2ecf20Sopenharmony_ci		} else if (baud > CD1400_MAX_SPEED) {
19918c2ecf20Sopenharmony_ci			baud = CD1400_MAX_SPEED;
19928c2ecf20Sopenharmony_ci		}
19938c2ecf20Sopenharmony_ci		/* find the baud index */
19948c2ecf20Sopenharmony_ci		for (i = 0; i < 20; i++) {
19958c2ecf20Sopenharmony_ci			if (baud == baud_table[i])
19968c2ecf20Sopenharmony_ci				break;
19978c2ecf20Sopenharmony_ci		}
19988c2ecf20Sopenharmony_ci		if (i == 20)
19998c2ecf20Sopenharmony_ci			i = 19;	/* CD1400_MAX_SPEED */
20008c2ecf20Sopenharmony_ci
20018c2ecf20Sopenharmony_ci		if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) ==
20028c2ecf20Sopenharmony_ci				ASYNC_SPD_CUST) {
20038c2ecf20Sopenharmony_ci			cyy_baud_calc(info, baud_rate);
20048c2ecf20Sopenharmony_ci		} else {
20058c2ecf20Sopenharmony_ci			if (info->chip_rev >= CD1400_REV_J) {
20068c2ecf20Sopenharmony_ci				/* It is a CD1400 rev. J or later */
20078c2ecf20Sopenharmony_ci				info->tbpr = baud_bpr_60[i];	/* Tx BPR */
20088c2ecf20Sopenharmony_ci				info->tco = baud_co_60[i];	/* Tx CO */
20098c2ecf20Sopenharmony_ci				info->rbpr = baud_bpr_60[i];	/* Rx BPR */
20108c2ecf20Sopenharmony_ci				info->rco = baud_co_60[i];	/* Rx CO */
20118c2ecf20Sopenharmony_ci			} else {
20128c2ecf20Sopenharmony_ci				info->tbpr = baud_bpr_25[i];	/* Tx BPR */
20138c2ecf20Sopenharmony_ci				info->tco = baud_co_25[i];	/* Tx CO */
20148c2ecf20Sopenharmony_ci				info->rbpr = baud_bpr_25[i];	/* Rx BPR */
20158c2ecf20Sopenharmony_ci				info->rco = baud_co_25[i];	/* Rx CO */
20168c2ecf20Sopenharmony_ci			}
20178c2ecf20Sopenharmony_ci		}
20188c2ecf20Sopenharmony_ci		if (baud_table[i] == 134) {
20198c2ecf20Sopenharmony_ci			/* get it right for 134.5 baud */
20208c2ecf20Sopenharmony_ci			info->timeout = (info->xmit_fifo_size * HZ * 30 / 269) +
20218c2ecf20Sopenharmony_ci					2;
20228c2ecf20Sopenharmony_ci		} else if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) ==
20238c2ecf20Sopenharmony_ci				ASYNC_SPD_CUST) {
20248c2ecf20Sopenharmony_ci			info->timeout = (info->xmit_fifo_size * HZ * 15 /
20258c2ecf20Sopenharmony_ci					baud_rate) + 2;
20268c2ecf20Sopenharmony_ci		} else if (baud_table[i]) {
20278c2ecf20Sopenharmony_ci			info->timeout = (info->xmit_fifo_size * HZ * 15 /
20288c2ecf20Sopenharmony_ci					baud_table[i]) + 2;
20298c2ecf20Sopenharmony_ci			/* this needs to be propagated into the card info */
20308c2ecf20Sopenharmony_ci		} else {
20318c2ecf20Sopenharmony_ci			info->timeout = 0;
20328c2ecf20Sopenharmony_ci		}
20338c2ecf20Sopenharmony_ci		/* By tradition (is it a standard?) a baud rate of zero
20348c2ecf20Sopenharmony_ci		   implies the line should be/has been closed.  A bit
20358c2ecf20Sopenharmony_ci		   later in this routine such a test is performed. */
20368c2ecf20Sopenharmony_ci
20378c2ecf20Sopenharmony_ci		/* byte size and parity */
20388c2ecf20Sopenharmony_ci		info->cor5 = 0;
20398c2ecf20Sopenharmony_ci		info->cor4 = 0;
20408c2ecf20Sopenharmony_ci		/* receive threshold */
20418c2ecf20Sopenharmony_ci		info->cor3 = (info->default_threshold ?
20428c2ecf20Sopenharmony_ci				info->default_threshold : baud_cor3[i]);
20438c2ecf20Sopenharmony_ci		info->cor2 = CyETC;
20448c2ecf20Sopenharmony_ci		switch (cflag & CSIZE) {
20458c2ecf20Sopenharmony_ci		case CS5:
20468c2ecf20Sopenharmony_ci			info->cor1 = Cy_5_BITS;
20478c2ecf20Sopenharmony_ci			break;
20488c2ecf20Sopenharmony_ci		case CS6:
20498c2ecf20Sopenharmony_ci			info->cor1 = Cy_6_BITS;
20508c2ecf20Sopenharmony_ci			break;
20518c2ecf20Sopenharmony_ci		case CS7:
20528c2ecf20Sopenharmony_ci			info->cor1 = Cy_7_BITS;
20538c2ecf20Sopenharmony_ci			break;
20548c2ecf20Sopenharmony_ci		case CS8:
20558c2ecf20Sopenharmony_ci			info->cor1 = Cy_8_BITS;
20568c2ecf20Sopenharmony_ci			break;
20578c2ecf20Sopenharmony_ci		}
20588c2ecf20Sopenharmony_ci		if (cflag & CSTOPB)
20598c2ecf20Sopenharmony_ci			info->cor1 |= Cy_2_STOP;
20608c2ecf20Sopenharmony_ci
20618c2ecf20Sopenharmony_ci		if (cflag & PARENB) {
20628c2ecf20Sopenharmony_ci			if (cflag & PARODD)
20638c2ecf20Sopenharmony_ci				info->cor1 |= CyPARITY_O;
20648c2ecf20Sopenharmony_ci			else
20658c2ecf20Sopenharmony_ci				info->cor1 |= CyPARITY_E;
20668c2ecf20Sopenharmony_ci		} else
20678c2ecf20Sopenharmony_ci			info->cor1 |= CyPARITY_NONE;
20688c2ecf20Sopenharmony_ci
20698c2ecf20Sopenharmony_ci		/* CTS flow control flag */
20708c2ecf20Sopenharmony_ci		tty_port_set_cts_flow(&info->port, cflag & CRTSCTS);
20718c2ecf20Sopenharmony_ci		if (cflag & CRTSCTS)
20728c2ecf20Sopenharmony_ci			info->cor2 |= CyCtsAE;
20738c2ecf20Sopenharmony_ci		else
20748c2ecf20Sopenharmony_ci			info->cor2 &= ~CyCtsAE;
20758c2ecf20Sopenharmony_ci		tty_port_set_check_carrier(&info->port, ~cflag & CLOCAL);
20768c2ecf20Sopenharmony_ci
20778c2ecf20Sopenharmony_ci	 /***********************************************
20788c2ecf20Sopenharmony_ci	    The hardware option, CyRtsAO, presents RTS when
20798c2ecf20Sopenharmony_ci	    the chip has characters to send.  Since most modems
20808c2ecf20Sopenharmony_ci	    use RTS as reverse (inbound) flow control, this
20818c2ecf20Sopenharmony_ci	    option is not used.  If inbound flow control is
20828c2ecf20Sopenharmony_ci	    necessary, DTR can be programmed to provide the
20838c2ecf20Sopenharmony_ci	    appropriate signals for use with a non-standard
20848c2ecf20Sopenharmony_ci	    cable.  Contact Marcio Saito for details.
20858c2ecf20Sopenharmony_ci	 ***********************************************/
20868c2ecf20Sopenharmony_ci
20878c2ecf20Sopenharmony_ci		channel &= 0x03;
20888c2ecf20Sopenharmony_ci
20898c2ecf20Sopenharmony_ci		spin_lock_irqsave(&card->card_lock, flags);
20908c2ecf20Sopenharmony_ci		cyy_writeb(info, CyCAR, channel);
20918c2ecf20Sopenharmony_ci
20928c2ecf20Sopenharmony_ci		/* tx and rx baud rate */
20938c2ecf20Sopenharmony_ci
20948c2ecf20Sopenharmony_ci		cyy_writeb(info, CyTCOR, info->tco);
20958c2ecf20Sopenharmony_ci		cyy_writeb(info, CyTBPR, info->tbpr);
20968c2ecf20Sopenharmony_ci		cyy_writeb(info, CyRCOR, info->rco);
20978c2ecf20Sopenharmony_ci		cyy_writeb(info, CyRBPR, info->rbpr);
20988c2ecf20Sopenharmony_ci
20998c2ecf20Sopenharmony_ci		/* set line characteristics  according configuration */
21008c2ecf20Sopenharmony_ci
21018c2ecf20Sopenharmony_ci		cyy_writeb(info, CySCHR1, START_CHAR(tty));
21028c2ecf20Sopenharmony_ci		cyy_writeb(info, CySCHR2, STOP_CHAR(tty));
21038c2ecf20Sopenharmony_ci		cyy_writeb(info, CyCOR1, info->cor1);
21048c2ecf20Sopenharmony_ci		cyy_writeb(info, CyCOR2, info->cor2);
21058c2ecf20Sopenharmony_ci		cyy_writeb(info, CyCOR3, info->cor3);
21068c2ecf20Sopenharmony_ci		cyy_writeb(info, CyCOR4, info->cor4);
21078c2ecf20Sopenharmony_ci		cyy_writeb(info, CyCOR5, info->cor5);
21088c2ecf20Sopenharmony_ci
21098c2ecf20Sopenharmony_ci		cyy_issue_cmd(info, CyCOR_CHANGE | CyCOR1ch | CyCOR2ch |
21108c2ecf20Sopenharmony_ci				CyCOR3ch);
21118c2ecf20Sopenharmony_ci
21128c2ecf20Sopenharmony_ci		/* !!! Is this needed? */
21138c2ecf20Sopenharmony_ci		cyy_writeb(info, CyCAR, channel);
21148c2ecf20Sopenharmony_ci		cyy_writeb(info, CyRTPR,
21158c2ecf20Sopenharmony_ci			(info->default_timeout ? info->default_timeout : 0x02));
21168c2ecf20Sopenharmony_ci		/* 10ms rx timeout */
21178c2ecf20Sopenharmony_ci
21188c2ecf20Sopenharmony_ci		cflags = CyCTS;
21198c2ecf20Sopenharmony_ci		if (!C_CLOCAL(tty))
21208c2ecf20Sopenharmony_ci			cflags |= CyDSR | CyRI | CyDCD;
21218c2ecf20Sopenharmony_ci		/* without modem intr */
21228c2ecf20Sopenharmony_ci		cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyMdmCh);
21238c2ecf20Sopenharmony_ci		/* act on 1->0 modem transitions */
21248c2ecf20Sopenharmony_ci		if ((cflag & CRTSCTS) && info->rflow)
21258c2ecf20Sopenharmony_ci			cyy_writeb(info, CyMCOR1, cflags | rflow_thr[i]);
21268c2ecf20Sopenharmony_ci		else
21278c2ecf20Sopenharmony_ci			cyy_writeb(info, CyMCOR1, cflags);
21288c2ecf20Sopenharmony_ci		/* act on 0->1 modem transitions */
21298c2ecf20Sopenharmony_ci		cyy_writeb(info, CyMCOR2, cflags);
21308c2ecf20Sopenharmony_ci
21318c2ecf20Sopenharmony_ci		if (i == 0)	/* baud rate is zero, turn off line */
21328c2ecf20Sopenharmony_ci			cyy_change_rts_dtr(info, 0, TIOCM_DTR);
21338c2ecf20Sopenharmony_ci		else
21348c2ecf20Sopenharmony_ci			cyy_change_rts_dtr(info, TIOCM_DTR, 0);
21358c2ecf20Sopenharmony_ci
21368c2ecf20Sopenharmony_ci		clear_bit(TTY_IO_ERROR, &tty->flags);
21378c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&card->card_lock, flags);
21388c2ecf20Sopenharmony_ci
21398c2ecf20Sopenharmony_ci	} else {
21408c2ecf20Sopenharmony_ci		struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl;
21418c2ecf20Sopenharmony_ci		__u32 sw_flow;
21428c2ecf20Sopenharmony_ci		int retval;
21438c2ecf20Sopenharmony_ci
21448c2ecf20Sopenharmony_ci		if (!cyz_is_loaded(card))
21458c2ecf20Sopenharmony_ci			return;
21468c2ecf20Sopenharmony_ci
21478c2ecf20Sopenharmony_ci		/* baud rate */
21488c2ecf20Sopenharmony_ci		baud = tty_get_baud_rate(tty);
21498c2ecf20Sopenharmony_ci		if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) ==
21508c2ecf20Sopenharmony_ci				ASYNC_SPD_CUST) {
21518c2ecf20Sopenharmony_ci			if (info->custom_divisor)
21528c2ecf20Sopenharmony_ci				baud_rate = info->baud / info->custom_divisor;
21538c2ecf20Sopenharmony_ci			else
21548c2ecf20Sopenharmony_ci				baud_rate = info->baud;
21558c2ecf20Sopenharmony_ci		} else if (baud > CYZ_MAX_SPEED) {
21568c2ecf20Sopenharmony_ci			baud = CYZ_MAX_SPEED;
21578c2ecf20Sopenharmony_ci		}
21588c2ecf20Sopenharmony_ci		cy_writel(&ch_ctrl->comm_baud, baud);
21598c2ecf20Sopenharmony_ci
21608c2ecf20Sopenharmony_ci		if (baud == 134) {
21618c2ecf20Sopenharmony_ci			/* get it right for 134.5 baud */
21628c2ecf20Sopenharmony_ci			info->timeout = (info->xmit_fifo_size * HZ * 30 / 269) +
21638c2ecf20Sopenharmony_ci					2;
21648c2ecf20Sopenharmony_ci		} else if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) ==
21658c2ecf20Sopenharmony_ci				ASYNC_SPD_CUST) {
21668c2ecf20Sopenharmony_ci			info->timeout = (info->xmit_fifo_size * HZ * 15 /
21678c2ecf20Sopenharmony_ci					baud_rate) + 2;
21688c2ecf20Sopenharmony_ci		} else if (baud) {
21698c2ecf20Sopenharmony_ci			info->timeout = (info->xmit_fifo_size * HZ * 15 /
21708c2ecf20Sopenharmony_ci					baud) + 2;
21718c2ecf20Sopenharmony_ci			/* this needs to be propagated into the card info */
21728c2ecf20Sopenharmony_ci		} else {
21738c2ecf20Sopenharmony_ci			info->timeout = 0;
21748c2ecf20Sopenharmony_ci		}
21758c2ecf20Sopenharmony_ci
21768c2ecf20Sopenharmony_ci		/* byte size and parity */
21778c2ecf20Sopenharmony_ci		switch (cflag & CSIZE) {
21788c2ecf20Sopenharmony_ci		case CS5:
21798c2ecf20Sopenharmony_ci			cy_writel(&ch_ctrl->comm_data_l, C_DL_CS5);
21808c2ecf20Sopenharmony_ci			break;
21818c2ecf20Sopenharmony_ci		case CS6:
21828c2ecf20Sopenharmony_ci			cy_writel(&ch_ctrl->comm_data_l, C_DL_CS6);
21838c2ecf20Sopenharmony_ci			break;
21848c2ecf20Sopenharmony_ci		case CS7:
21858c2ecf20Sopenharmony_ci			cy_writel(&ch_ctrl->comm_data_l, C_DL_CS7);
21868c2ecf20Sopenharmony_ci			break;
21878c2ecf20Sopenharmony_ci		case CS8:
21888c2ecf20Sopenharmony_ci			cy_writel(&ch_ctrl->comm_data_l, C_DL_CS8);
21898c2ecf20Sopenharmony_ci			break;
21908c2ecf20Sopenharmony_ci		}
21918c2ecf20Sopenharmony_ci		if (cflag & CSTOPB) {
21928c2ecf20Sopenharmony_ci			cy_writel(&ch_ctrl->comm_data_l,
21938c2ecf20Sopenharmony_ci				  readl(&ch_ctrl->comm_data_l) | C_DL_2STOP);
21948c2ecf20Sopenharmony_ci		} else {
21958c2ecf20Sopenharmony_ci			cy_writel(&ch_ctrl->comm_data_l,
21968c2ecf20Sopenharmony_ci				  readl(&ch_ctrl->comm_data_l) | C_DL_1STOP);
21978c2ecf20Sopenharmony_ci		}
21988c2ecf20Sopenharmony_ci		if (cflag & PARENB) {
21998c2ecf20Sopenharmony_ci			if (cflag & PARODD)
22008c2ecf20Sopenharmony_ci				cy_writel(&ch_ctrl->comm_parity, C_PR_ODD);
22018c2ecf20Sopenharmony_ci			else
22028c2ecf20Sopenharmony_ci				cy_writel(&ch_ctrl->comm_parity, C_PR_EVEN);
22038c2ecf20Sopenharmony_ci		} else
22048c2ecf20Sopenharmony_ci			cy_writel(&ch_ctrl->comm_parity, C_PR_NONE);
22058c2ecf20Sopenharmony_ci
22068c2ecf20Sopenharmony_ci		/* CTS flow control flag */
22078c2ecf20Sopenharmony_ci		if (cflag & CRTSCTS) {
22088c2ecf20Sopenharmony_ci			cy_writel(&ch_ctrl->hw_flow,
22098c2ecf20Sopenharmony_ci				readl(&ch_ctrl->hw_flow) | C_RS_CTS | C_RS_RTS);
22108c2ecf20Sopenharmony_ci		} else {
22118c2ecf20Sopenharmony_ci			cy_writel(&ch_ctrl->hw_flow, readl(&ch_ctrl->hw_flow) &
22128c2ecf20Sopenharmony_ci					~(C_RS_CTS | C_RS_RTS));
22138c2ecf20Sopenharmony_ci		}
22148c2ecf20Sopenharmony_ci		/* As the HW flow control is done in firmware, the driver
22158c2ecf20Sopenharmony_ci		   doesn't need to care about it */
22168c2ecf20Sopenharmony_ci		tty_port_set_cts_flow(&info->port, 0);
22178c2ecf20Sopenharmony_ci
22188c2ecf20Sopenharmony_ci		/* XON/XOFF/XANY flow control flags */
22198c2ecf20Sopenharmony_ci		sw_flow = 0;
22208c2ecf20Sopenharmony_ci		if (iflag & IXON) {
22218c2ecf20Sopenharmony_ci			sw_flow |= C_FL_OXX;
22228c2ecf20Sopenharmony_ci			if (iflag & IXANY)
22238c2ecf20Sopenharmony_ci				sw_flow |= C_FL_OIXANY;
22248c2ecf20Sopenharmony_ci		}
22258c2ecf20Sopenharmony_ci		cy_writel(&ch_ctrl->sw_flow, sw_flow);
22268c2ecf20Sopenharmony_ci
22278c2ecf20Sopenharmony_ci		retval = cyz_issue_cmd(card, channel, C_CM_IOCTL, 0L);
22288c2ecf20Sopenharmony_ci		if (retval != 0) {
22298c2ecf20Sopenharmony_ci			printk(KERN_ERR "cyc:set_line_char retval on ttyC%d "
22308c2ecf20Sopenharmony_ci				"was %x\n", info->line, retval);
22318c2ecf20Sopenharmony_ci		}
22328c2ecf20Sopenharmony_ci
22338c2ecf20Sopenharmony_ci		/* CD sensitivity */
22348c2ecf20Sopenharmony_ci		tty_port_set_check_carrier(&info->port, ~cflag & CLOCAL);
22358c2ecf20Sopenharmony_ci
22368c2ecf20Sopenharmony_ci		if (baud == 0) {	/* baud rate is zero, turn off line */
22378c2ecf20Sopenharmony_ci			cy_writel(&ch_ctrl->rs_control,
22388c2ecf20Sopenharmony_ci				  readl(&ch_ctrl->rs_control) & ~C_RS_DTR);
22398c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_DTR
22408c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "cyc:set_line_char dropping Z DTR\n");
22418c2ecf20Sopenharmony_ci#endif
22428c2ecf20Sopenharmony_ci		} else {
22438c2ecf20Sopenharmony_ci			cy_writel(&ch_ctrl->rs_control,
22448c2ecf20Sopenharmony_ci				  readl(&ch_ctrl->rs_control) | C_RS_DTR);
22458c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_DTR
22468c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "cyc:set_line_char raising Z DTR\n");
22478c2ecf20Sopenharmony_ci#endif
22488c2ecf20Sopenharmony_ci		}
22498c2ecf20Sopenharmony_ci
22508c2ecf20Sopenharmony_ci		retval = cyz_issue_cmd(card, channel, C_CM_IOCTLM, 0L);
22518c2ecf20Sopenharmony_ci		if (retval != 0) {
22528c2ecf20Sopenharmony_ci			printk(KERN_ERR "cyc:set_line_char(2) retval on ttyC%d "
22538c2ecf20Sopenharmony_ci				"was %x\n", info->line, retval);
22548c2ecf20Sopenharmony_ci		}
22558c2ecf20Sopenharmony_ci
22568c2ecf20Sopenharmony_ci		clear_bit(TTY_IO_ERROR, &tty->flags);
22578c2ecf20Sopenharmony_ci	}
22588c2ecf20Sopenharmony_ci}				/* set_line_char */
22598c2ecf20Sopenharmony_ci
22608c2ecf20Sopenharmony_cistatic int cy_get_serial_info(struct tty_struct *tty,
22618c2ecf20Sopenharmony_ci				struct serial_struct *ss)
22628c2ecf20Sopenharmony_ci{
22638c2ecf20Sopenharmony_ci	struct cyclades_port *info = tty->driver_data;
22648c2ecf20Sopenharmony_ci	struct cyclades_card *cinfo = info->card;
22658c2ecf20Sopenharmony_ci
22668c2ecf20Sopenharmony_ci	if (serial_paranoia_check(info, tty->name, "cy_ioctl"))
22678c2ecf20Sopenharmony_ci		return -ENODEV;
22688c2ecf20Sopenharmony_ci	ss->type = info->type;
22698c2ecf20Sopenharmony_ci	ss->line = info->line;
22708c2ecf20Sopenharmony_ci	ss->port = (info->card - cy_card) * 0x100 + info->line -
22718c2ecf20Sopenharmony_ci			cinfo->first_line;
22728c2ecf20Sopenharmony_ci	ss->irq = cinfo->irq;
22738c2ecf20Sopenharmony_ci	ss->flags = info->port.flags;
22748c2ecf20Sopenharmony_ci	ss->close_delay = info->port.close_delay;
22758c2ecf20Sopenharmony_ci	ss->closing_wait = info->port.closing_wait;
22768c2ecf20Sopenharmony_ci	ss->baud_base = info->baud;
22778c2ecf20Sopenharmony_ci	ss->custom_divisor = info->custom_divisor;
22788c2ecf20Sopenharmony_ci	return 0;
22798c2ecf20Sopenharmony_ci}
22808c2ecf20Sopenharmony_ci
22818c2ecf20Sopenharmony_cistatic int cy_set_serial_info(struct tty_struct *tty,
22828c2ecf20Sopenharmony_ci				struct serial_struct *ss)
22838c2ecf20Sopenharmony_ci{
22848c2ecf20Sopenharmony_ci	struct cyclades_port *info = tty->driver_data;
22858c2ecf20Sopenharmony_ci	int old_flags;
22868c2ecf20Sopenharmony_ci	int ret;
22878c2ecf20Sopenharmony_ci
22888c2ecf20Sopenharmony_ci	if (serial_paranoia_check(info, tty->name, "cy_ioctl"))
22898c2ecf20Sopenharmony_ci		return -ENODEV;
22908c2ecf20Sopenharmony_ci
22918c2ecf20Sopenharmony_ci	mutex_lock(&info->port.mutex);
22928c2ecf20Sopenharmony_ci
22938c2ecf20Sopenharmony_ci	old_flags = info->port.flags;
22948c2ecf20Sopenharmony_ci
22958c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN)) {
22968c2ecf20Sopenharmony_ci		if (ss->close_delay != info->port.close_delay ||
22978c2ecf20Sopenharmony_ci				ss->baud_base != info->baud ||
22988c2ecf20Sopenharmony_ci				(ss->flags & ASYNC_FLAGS &
22998c2ecf20Sopenharmony_ci					~ASYNC_USR_MASK) !=
23008c2ecf20Sopenharmony_ci				(info->port.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK))
23018c2ecf20Sopenharmony_ci		{
23028c2ecf20Sopenharmony_ci			mutex_unlock(&info->port.mutex);
23038c2ecf20Sopenharmony_ci			return -EPERM;
23048c2ecf20Sopenharmony_ci		}
23058c2ecf20Sopenharmony_ci		info->port.flags = (info->port.flags & ~ASYNC_USR_MASK) |
23068c2ecf20Sopenharmony_ci				(ss->flags & ASYNC_USR_MASK);
23078c2ecf20Sopenharmony_ci		info->baud = ss->baud_base;
23088c2ecf20Sopenharmony_ci		info->custom_divisor = ss->custom_divisor;
23098c2ecf20Sopenharmony_ci		goto check_and_exit;
23108c2ecf20Sopenharmony_ci	}
23118c2ecf20Sopenharmony_ci
23128c2ecf20Sopenharmony_ci	/*
23138c2ecf20Sopenharmony_ci	 * OK, past this point, all the error checking has been done.
23148c2ecf20Sopenharmony_ci	 * At this point, we start making changes.....
23158c2ecf20Sopenharmony_ci	 */
23168c2ecf20Sopenharmony_ci
23178c2ecf20Sopenharmony_ci	info->baud = ss->baud_base;
23188c2ecf20Sopenharmony_ci	info->custom_divisor = ss->custom_divisor;
23198c2ecf20Sopenharmony_ci	info->port.flags = (info->port.flags & ~ASYNC_FLAGS) |
23208c2ecf20Sopenharmony_ci			(ss->flags & ASYNC_FLAGS);
23218c2ecf20Sopenharmony_ci	info->port.close_delay = ss->close_delay * HZ / 100;
23228c2ecf20Sopenharmony_ci	info->port.closing_wait = ss->closing_wait * HZ / 100;
23238c2ecf20Sopenharmony_ci
23248c2ecf20Sopenharmony_cicheck_and_exit:
23258c2ecf20Sopenharmony_ci	if (tty_port_initialized(&info->port)) {
23268c2ecf20Sopenharmony_ci		if ((ss->flags ^ old_flags) & ASYNC_SPD_MASK) {
23278c2ecf20Sopenharmony_ci			/* warn about deprecation unless clearing */
23288c2ecf20Sopenharmony_ci			if (ss->flags & ASYNC_SPD_MASK)
23298c2ecf20Sopenharmony_ci				dev_warn_ratelimited(tty->dev, "use of SPD flags is deprecated\n");
23308c2ecf20Sopenharmony_ci		}
23318c2ecf20Sopenharmony_ci		cy_set_line_char(info, tty);
23328c2ecf20Sopenharmony_ci		ret = 0;
23338c2ecf20Sopenharmony_ci	} else {
23348c2ecf20Sopenharmony_ci		ret = cy_startup(info, tty);
23358c2ecf20Sopenharmony_ci	}
23368c2ecf20Sopenharmony_ci	mutex_unlock(&info->port.mutex);
23378c2ecf20Sopenharmony_ci	return ret;
23388c2ecf20Sopenharmony_ci}				/* set_serial_info */
23398c2ecf20Sopenharmony_ci
23408c2ecf20Sopenharmony_ci/*
23418c2ecf20Sopenharmony_ci * get_lsr_info - get line status register info
23428c2ecf20Sopenharmony_ci *
23438c2ecf20Sopenharmony_ci * Purpose: Let user call ioctl() to get info when the UART physically
23448c2ecf20Sopenharmony_ci *	    is emptied.  On bus types like RS485, the transmitter must
23458c2ecf20Sopenharmony_ci *	    release the bus after transmitting. This must be done when
23468c2ecf20Sopenharmony_ci *	    the transmit shift register is empty, not be done when the
23478c2ecf20Sopenharmony_ci *	    transmit holding register is empty.  This functionality
23488c2ecf20Sopenharmony_ci *	    allows an RS485 driver to be written in user space.
23498c2ecf20Sopenharmony_ci */
23508c2ecf20Sopenharmony_cistatic int get_lsr_info(struct cyclades_port *info, unsigned int __user *value)
23518c2ecf20Sopenharmony_ci{
23528c2ecf20Sopenharmony_ci	struct cyclades_card *card = info->card;
23538c2ecf20Sopenharmony_ci	unsigned int result;
23548c2ecf20Sopenharmony_ci	unsigned long flags;
23558c2ecf20Sopenharmony_ci	u8 status;
23568c2ecf20Sopenharmony_ci
23578c2ecf20Sopenharmony_ci	if (!cy_is_Z(card)) {
23588c2ecf20Sopenharmony_ci		spin_lock_irqsave(&card->card_lock, flags);
23598c2ecf20Sopenharmony_ci		status = cyy_readb(info, CySRER) & (CyTxRdy | CyTxMpty);
23608c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&card->card_lock, flags);
23618c2ecf20Sopenharmony_ci		result = (status ? 0 : TIOCSER_TEMT);
23628c2ecf20Sopenharmony_ci	} else {
23638c2ecf20Sopenharmony_ci		/* Not supported yet */
23648c2ecf20Sopenharmony_ci		return -EINVAL;
23658c2ecf20Sopenharmony_ci	}
23668c2ecf20Sopenharmony_ci	return put_user(result, value);
23678c2ecf20Sopenharmony_ci}
23688c2ecf20Sopenharmony_ci
23698c2ecf20Sopenharmony_cistatic int cy_tiocmget(struct tty_struct *tty)
23708c2ecf20Sopenharmony_ci{
23718c2ecf20Sopenharmony_ci	struct cyclades_port *info = tty->driver_data;
23728c2ecf20Sopenharmony_ci	struct cyclades_card *card;
23738c2ecf20Sopenharmony_ci	int result;
23748c2ecf20Sopenharmony_ci
23758c2ecf20Sopenharmony_ci	if (serial_paranoia_check(info, tty->name, __func__))
23768c2ecf20Sopenharmony_ci		return -ENODEV;
23778c2ecf20Sopenharmony_ci
23788c2ecf20Sopenharmony_ci	card = info->card;
23798c2ecf20Sopenharmony_ci
23808c2ecf20Sopenharmony_ci	if (!cy_is_Z(card)) {
23818c2ecf20Sopenharmony_ci		unsigned long flags;
23828c2ecf20Sopenharmony_ci		int channel = info->line - card->first_line;
23838c2ecf20Sopenharmony_ci		u8 status;
23848c2ecf20Sopenharmony_ci
23858c2ecf20Sopenharmony_ci		spin_lock_irqsave(&card->card_lock, flags);
23868c2ecf20Sopenharmony_ci		cyy_writeb(info, CyCAR, channel & 0x03);
23878c2ecf20Sopenharmony_ci		status = cyy_readb(info, CyMSVR1);
23888c2ecf20Sopenharmony_ci		status |= cyy_readb(info, CyMSVR2);
23898c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&card->card_lock, flags);
23908c2ecf20Sopenharmony_ci
23918c2ecf20Sopenharmony_ci		if (info->rtsdtr_inv) {
23928c2ecf20Sopenharmony_ci			result = ((status & CyRTS) ? TIOCM_DTR : 0) |
23938c2ecf20Sopenharmony_ci				((status & CyDTR) ? TIOCM_RTS : 0);
23948c2ecf20Sopenharmony_ci		} else {
23958c2ecf20Sopenharmony_ci			result = ((status & CyRTS) ? TIOCM_RTS : 0) |
23968c2ecf20Sopenharmony_ci				((status & CyDTR) ? TIOCM_DTR : 0);
23978c2ecf20Sopenharmony_ci		}
23988c2ecf20Sopenharmony_ci		result |= ((status & CyDCD) ? TIOCM_CAR : 0) |
23998c2ecf20Sopenharmony_ci			((status & CyRI) ? TIOCM_RNG : 0) |
24008c2ecf20Sopenharmony_ci			((status & CyDSR) ? TIOCM_DSR : 0) |
24018c2ecf20Sopenharmony_ci			((status & CyCTS) ? TIOCM_CTS : 0);
24028c2ecf20Sopenharmony_ci	} else {
24038c2ecf20Sopenharmony_ci		u32 lstatus;
24048c2ecf20Sopenharmony_ci
24058c2ecf20Sopenharmony_ci		if (!cyz_is_loaded(card)) {
24068c2ecf20Sopenharmony_ci			result = -ENODEV;
24078c2ecf20Sopenharmony_ci			goto end;
24088c2ecf20Sopenharmony_ci		}
24098c2ecf20Sopenharmony_ci
24108c2ecf20Sopenharmony_ci		lstatus = readl(&info->u.cyz.ch_ctrl->rs_status);
24118c2ecf20Sopenharmony_ci		result = ((lstatus & C_RS_RTS) ? TIOCM_RTS : 0) |
24128c2ecf20Sopenharmony_ci			((lstatus & C_RS_DTR) ? TIOCM_DTR : 0) |
24138c2ecf20Sopenharmony_ci			((lstatus & C_RS_DCD) ? TIOCM_CAR : 0) |
24148c2ecf20Sopenharmony_ci			((lstatus & C_RS_RI) ? TIOCM_RNG : 0) |
24158c2ecf20Sopenharmony_ci			((lstatus & C_RS_DSR) ? TIOCM_DSR : 0) |
24168c2ecf20Sopenharmony_ci			((lstatus & C_RS_CTS) ? TIOCM_CTS : 0);
24178c2ecf20Sopenharmony_ci	}
24188c2ecf20Sopenharmony_ciend:
24198c2ecf20Sopenharmony_ci	return result;
24208c2ecf20Sopenharmony_ci}				/* cy_tiomget */
24218c2ecf20Sopenharmony_ci
24228c2ecf20Sopenharmony_cistatic int
24238c2ecf20Sopenharmony_cicy_tiocmset(struct tty_struct *tty,
24248c2ecf20Sopenharmony_ci		unsigned int set, unsigned int clear)
24258c2ecf20Sopenharmony_ci{
24268c2ecf20Sopenharmony_ci	struct cyclades_port *info = tty->driver_data;
24278c2ecf20Sopenharmony_ci	struct cyclades_card *card;
24288c2ecf20Sopenharmony_ci	unsigned long flags;
24298c2ecf20Sopenharmony_ci
24308c2ecf20Sopenharmony_ci	if (serial_paranoia_check(info, tty->name, __func__))
24318c2ecf20Sopenharmony_ci		return -ENODEV;
24328c2ecf20Sopenharmony_ci
24338c2ecf20Sopenharmony_ci	card = info->card;
24348c2ecf20Sopenharmony_ci	if (!cy_is_Z(card)) {
24358c2ecf20Sopenharmony_ci		spin_lock_irqsave(&card->card_lock, flags);
24368c2ecf20Sopenharmony_ci		cyy_change_rts_dtr(info, set, clear);
24378c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&card->card_lock, flags);
24388c2ecf20Sopenharmony_ci	} else {
24398c2ecf20Sopenharmony_ci		struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl;
24408c2ecf20Sopenharmony_ci		int retval, channel = info->line - card->first_line;
24418c2ecf20Sopenharmony_ci		u32 rs;
24428c2ecf20Sopenharmony_ci
24438c2ecf20Sopenharmony_ci		if (!cyz_is_loaded(card))
24448c2ecf20Sopenharmony_ci			return -ENODEV;
24458c2ecf20Sopenharmony_ci
24468c2ecf20Sopenharmony_ci		spin_lock_irqsave(&card->card_lock, flags);
24478c2ecf20Sopenharmony_ci		rs = readl(&ch_ctrl->rs_control);
24488c2ecf20Sopenharmony_ci		if (set & TIOCM_RTS)
24498c2ecf20Sopenharmony_ci			rs |= C_RS_RTS;
24508c2ecf20Sopenharmony_ci		if (clear & TIOCM_RTS)
24518c2ecf20Sopenharmony_ci			rs &= ~C_RS_RTS;
24528c2ecf20Sopenharmony_ci		if (set & TIOCM_DTR) {
24538c2ecf20Sopenharmony_ci			rs |= C_RS_DTR;
24548c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_DTR
24558c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "cyc:set_modem_info raising Z DTR\n");
24568c2ecf20Sopenharmony_ci#endif
24578c2ecf20Sopenharmony_ci		}
24588c2ecf20Sopenharmony_ci		if (clear & TIOCM_DTR) {
24598c2ecf20Sopenharmony_ci			rs &= ~C_RS_DTR;
24608c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_DTR
24618c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "cyc:set_modem_info clearing "
24628c2ecf20Sopenharmony_ci				"Z DTR\n");
24638c2ecf20Sopenharmony_ci#endif
24648c2ecf20Sopenharmony_ci		}
24658c2ecf20Sopenharmony_ci		cy_writel(&ch_ctrl->rs_control, rs);
24668c2ecf20Sopenharmony_ci		retval = cyz_issue_cmd(card, channel, C_CM_IOCTLM, 0L);
24678c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&card->card_lock, flags);
24688c2ecf20Sopenharmony_ci		if (retval != 0) {
24698c2ecf20Sopenharmony_ci			printk(KERN_ERR "cyc:set_modem_info retval on ttyC%d "
24708c2ecf20Sopenharmony_ci				"was %x\n", info->line, retval);
24718c2ecf20Sopenharmony_ci		}
24728c2ecf20Sopenharmony_ci	}
24738c2ecf20Sopenharmony_ci	return 0;
24748c2ecf20Sopenharmony_ci}
24758c2ecf20Sopenharmony_ci
24768c2ecf20Sopenharmony_ci/*
24778c2ecf20Sopenharmony_ci * cy_break() --- routine which turns the break handling on or off
24788c2ecf20Sopenharmony_ci */
24798c2ecf20Sopenharmony_cistatic int cy_break(struct tty_struct *tty, int break_state)
24808c2ecf20Sopenharmony_ci{
24818c2ecf20Sopenharmony_ci	struct cyclades_port *info = tty->driver_data;
24828c2ecf20Sopenharmony_ci	struct cyclades_card *card;
24838c2ecf20Sopenharmony_ci	unsigned long flags;
24848c2ecf20Sopenharmony_ci	int retval = 0;
24858c2ecf20Sopenharmony_ci
24868c2ecf20Sopenharmony_ci	if (serial_paranoia_check(info, tty->name, "cy_break"))
24878c2ecf20Sopenharmony_ci		return -EINVAL;
24888c2ecf20Sopenharmony_ci
24898c2ecf20Sopenharmony_ci	card = info->card;
24908c2ecf20Sopenharmony_ci
24918c2ecf20Sopenharmony_ci	spin_lock_irqsave(&card->card_lock, flags);
24928c2ecf20Sopenharmony_ci	if (!cy_is_Z(card)) {
24938c2ecf20Sopenharmony_ci		/* Let the transmit ISR take care of this (since it
24948c2ecf20Sopenharmony_ci		   requires stuffing characters into the output stream).
24958c2ecf20Sopenharmony_ci		 */
24968c2ecf20Sopenharmony_ci		if (break_state == -1) {
24978c2ecf20Sopenharmony_ci			if (!info->breakon) {
24988c2ecf20Sopenharmony_ci				info->breakon = 1;
24998c2ecf20Sopenharmony_ci				if (!info->xmit_cnt) {
25008c2ecf20Sopenharmony_ci					spin_unlock_irqrestore(&card->card_lock, flags);
25018c2ecf20Sopenharmony_ci					start_xmit(info);
25028c2ecf20Sopenharmony_ci					spin_lock_irqsave(&card->card_lock, flags);
25038c2ecf20Sopenharmony_ci				}
25048c2ecf20Sopenharmony_ci			}
25058c2ecf20Sopenharmony_ci		} else {
25068c2ecf20Sopenharmony_ci			if (!info->breakoff) {
25078c2ecf20Sopenharmony_ci				info->breakoff = 1;
25088c2ecf20Sopenharmony_ci				if (!info->xmit_cnt) {
25098c2ecf20Sopenharmony_ci					spin_unlock_irqrestore(&card->card_lock, flags);
25108c2ecf20Sopenharmony_ci					start_xmit(info);
25118c2ecf20Sopenharmony_ci					spin_lock_irqsave(&card->card_lock, flags);
25128c2ecf20Sopenharmony_ci				}
25138c2ecf20Sopenharmony_ci			}
25148c2ecf20Sopenharmony_ci		}
25158c2ecf20Sopenharmony_ci	} else {
25168c2ecf20Sopenharmony_ci		if (break_state == -1) {
25178c2ecf20Sopenharmony_ci			retval = cyz_issue_cmd(card,
25188c2ecf20Sopenharmony_ci				info->line - card->first_line,
25198c2ecf20Sopenharmony_ci				C_CM_SET_BREAK, 0L);
25208c2ecf20Sopenharmony_ci			if (retval != 0) {
25218c2ecf20Sopenharmony_ci				printk(KERN_ERR "cyc:cy_break (set) retval on "
25228c2ecf20Sopenharmony_ci					"ttyC%d was %x\n", info->line, retval);
25238c2ecf20Sopenharmony_ci			}
25248c2ecf20Sopenharmony_ci		} else {
25258c2ecf20Sopenharmony_ci			retval = cyz_issue_cmd(card,
25268c2ecf20Sopenharmony_ci				info->line - card->first_line,
25278c2ecf20Sopenharmony_ci				C_CM_CLR_BREAK, 0L);
25288c2ecf20Sopenharmony_ci			if (retval != 0) {
25298c2ecf20Sopenharmony_ci				printk(KERN_DEBUG "cyc:cy_break (clr) retval "
25308c2ecf20Sopenharmony_ci					"on ttyC%d was %x\n", info->line,
25318c2ecf20Sopenharmony_ci					retval);
25328c2ecf20Sopenharmony_ci			}
25338c2ecf20Sopenharmony_ci		}
25348c2ecf20Sopenharmony_ci	}
25358c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&card->card_lock, flags);
25368c2ecf20Sopenharmony_ci	return retval;
25378c2ecf20Sopenharmony_ci}				/* cy_break */
25388c2ecf20Sopenharmony_ci
25398c2ecf20Sopenharmony_cistatic int set_threshold(struct cyclades_port *info, unsigned long value)
25408c2ecf20Sopenharmony_ci{
25418c2ecf20Sopenharmony_ci	struct cyclades_card *card = info->card;
25428c2ecf20Sopenharmony_ci	unsigned long flags;
25438c2ecf20Sopenharmony_ci
25448c2ecf20Sopenharmony_ci	if (!cy_is_Z(card)) {
25458c2ecf20Sopenharmony_ci		info->cor3 &= ~CyREC_FIFO;
25468c2ecf20Sopenharmony_ci		info->cor3 |= value & CyREC_FIFO;
25478c2ecf20Sopenharmony_ci
25488c2ecf20Sopenharmony_ci		spin_lock_irqsave(&card->card_lock, flags);
25498c2ecf20Sopenharmony_ci		cyy_writeb(info, CyCOR3, info->cor3);
25508c2ecf20Sopenharmony_ci		cyy_issue_cmd(info, CyCOR_CHANGE | CyCOR3ch);
25518c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&card->card_lock, flags);
25528c2ecf20Sopenharmony_ci	}
25538c2ecf20Sopenharmony_ci	return 0;
25548c2ecf20Sopenharmony_ci}				/* set_threshold */
25558c2ecf20Sopenharmony_ci
25568c2ecf20Sopenharmony_cistatic int get_threshold(struct cyclades_port *info,
25578c2ecf20Sopenharmony_ci						unsigned long __user *value)
25588c2ecf20Sopenharmony_ci{
25598c2ecf20Sopenharmony_ci	struct cyclades_card *card = info->card;
25608c2ecf20Sopenharmony_ci
25618c2ecf20Sopenharmony_ci	if (!cy_is_Z(card)) {
25628c2ecf20Sopenharmony_ci		u8 tmp = cyy_readb(info, CyCOR3) & CyREC_FIFO;
25638c2ecf20Sopenharmony_ci		return put_user(tmp, value);
25648c2ecf20Sopenharmony_ci	}
25658c2ecf20Sopenharmony_ci	return 0;
25668c2ecf20Sopenharmony_ci}				/* get_threshold */
25678c2ecf20Sopenharmony_ci
25688c2ecf20Sopenharmony_cistatic int set_timeout(struct cyclades_port *info, unsigned long value)
25698c2ecf20Sopenharmony_ci{
25708c2ecf20Sopenharmony_ci	struct cyclades_card *card = info->card;
25718c2ecf20Sopenharmony_ci	unsigned long flags;
25728c2ecf20Sopenharmony_ci
25738c2ecf20Sopenharmony_ci	if (!cy_is_Z(card)) {
25748c2ecf20Sopenharmony_ci		spin_lock_irqsave(&card->card_lock, flags);
25758c2ecf20Sopenharmony_ci		cyy_writeb(info, CyRTPR, value & 0xff);
25768c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&card->card_lock, flags);
25778c2ecf20Sopenharmony_ci	}
25788c2ecf20Sopenharmony_ci	return 0;
25798c2ecf20Sopenharmony_ci}				/* set_timeout */
25808c2ecf20Sopenharmony_ci
25818c2ecf20Sopenharmony_cistatic int get_timeout(struct cyclades_port *info,
25828c2ecf20Sopenharmony_ci						unsigned long __user *value)
25838c2ecf20Sopenharmony_ci{
25848c2ecf20Sopenharmony_ci	struct cyclades_card *card = info->card;
25858c2ecf20Sopenharmony_ci
25868c2ecf20Sopenharmony_ci	if (!cy_is_Z(card)) {
25878c2ecf20Sopenharmony_ci		u8 tmp = cyy_readb(info, CyRTPR);
25888c2ecf20Sopenharmony_ci		return put_user(tmp, value);
25898c2ecf20Sopenharmony_ci	}
25908c2ecf20Sopenharmony_ci	return 0;
25918c2ecf20Sopenharmony_ci}				/* get_timeout */
25928c2ecf20Sopenharmony_ci
25938c2ecf20Sopenharmony_cistatic int cy_cflags_changed(struct cyclades_port *info, unsigned long arg,
25948c2ecf20Sopenharmony_ci		struct cyclades_icount *cprev)
25958c2ecf20Sopenharmony_ci{
25968c2ecf20Sopenharmony_ci	struct cyclades_icount cnow;
25978c2ecf20Sopenharmony_ci	unsigned long flags;
25988c2ecf20Sopenharmony_ci	int ret;
25998c2ecf20Sopenharmony_ci
26008c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->card->card_lock, flags);
26018c2ecf20Sopenharmony_ci	cnow = info->icount;	/* atomic copy */
26028c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->card->card_lock, flags);
26038c2ecf20Sopenharmony_ci
26048c2ecf20Sopenharmony_ci	ret =	((arg & TIOCM_RNG) && (cnow.rng != cprev->rng)) ||
26058c2ecf20Sopenharmony_ci		((arg & TIOCM_DSR) && (cnow.dsr != cprev->dsr)) ||
26068c2ecf20Sopenharmony_ci		((arg & TIOCM_CD)  && (cnow.dcd != cprev->dcd)) ||
26078c2ecf20Sopenharmony_ci		((arg & TIOCM_CTS) && (cnow.cts != cprev->cts));
26088c2ecf20Sopenharmony_ci
26098c2ecf20Sopenharmony_ci	*cprev = cnow;
26108c2ecf20Sopenharmony_ci
26118c2ecf20Sopenharmony_ci	return ret;
26128c2ecf20Sopenharmony_ci}
26138c2ecf20Sopenharmony_ci
26148c2ecf20Sopenharmony_ci/*
26158c2ecf20Sopenharmony_ci * This routine allows the tty driver to implement device-
26168c2ecf20Sopenharmony_ci * specific ioctl's.  If the ioctl number passed in cmd is
26178c2ecf20Sopenharmony_ci * not recognized by the driver, it should return ENOIOCTLCMD.
26188c2ecf20Sopenharmony_ci */
26198c2ecf20Sopenharmony_cistatic int
26208c2ecf20Sopenharmony_cicy_ioctl(struct tty_struct *tty,
26218c2ecf20Sopenharmony_ci	 unsigned int cmd, unsigned long arg)
26228c2ecf20Sopenharmony_ci{
26238c2ecf20Sopenharmony_ci	struct cyclades_port *info = tty->driver_data;
26248c2ecf20Sopenharmony_ci	struct cyclades_icount cnow;	/* kernel counter temps */
26258c2ecf20Sopenharmony_ci	int ret_val = 0;
26268c2ecf20Sopenharmony_ci	unsigned long flags;
26278c2ecf20Sopenharmony_ci	void __user *argp = (void __user *)arg;
26288c2ecf20Sopenharmony_ci
26298c2ecf20Sopenharmony_ci	if (serial_paranoia_check(info, tty->name, "cy_ioctl"))
26308c2ecf20Sopenharmony_ci		return -ENODEV;
26318c2ecf20Sopenharmony_ci
26328c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_OTHER
26338c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "cyc:cy_ioctl ttyC%d, cmd = %x arg = %lx\n",
26348c2ecf20Sopenharmony_ci		info->line, cmd, arg);
26358c2ecf20Sopenharmony_ci#endif
26368c2ecf20Sopenharmony_ci
26378c2ecf20Sopenharmony_ci	switch (cmd) {
26388c2ecf20Sopenharmony_ci	case CYGETMON:
26398c2ecf20Sopenharmony_ci		if (copy_to_user(argp, &info->mon, sizeof(info->mon))) {
26408c2ecf20Sopenharmony_ci			ret_val = -EFAULT;
26418c2ecf20Sopenharmony_ci			break;
26428c2ecf20Sopenharmony_ci		}
26438c2ecf20Sopenharmony_ci		memset(&info->mon, 0, sizeof(info->mon));
26448c2ecf20Sopenharmony_ci		break;
26458c2ecf20Sopenharmony_ci	case CYGETTHRESH:
26468c2ecf20Sopenharmony_ci		ret_val = get_threshold(info, argp);
26478c2ecf20Sopenharmony_ci		break;
26488c2ecf20Sopenharmony_ci	case CYSETTHRESH:
26498c2ecf20Sopenharmony_ci		ret_val = set_threshold(info, arg);
26508c2ecf20Sopenharmony_ci		break;
26518c2ecf20Sopenharmony_ci	case CYGETDEFTHRESH:
26528c2ecf20Sopenharmony_ci		ret_val = put_user(info->default_threshold,
26538c2ecf20Sopenharmony_ci				(unsigned long __user *)argp);
26548c2ecf20Sopenharmony_ci		break;
26558c2ecf20Sopenharmony_ci	case CYSETDEFTHRESH:
26568c2ecf20Sopenharmony_ci		info->default_threshold = arg & 0x0f;
26578c2ecf20Sopenharmony_ci		break;
26588c2ecf20Sopenharmony_ci	case CYGETTIMEOUT:
26598c2ecf20Sopenharmony_ci		ret_val = get_timeout(info, argp);
26608c2ecf20Sopenharmony_ci		break;
26618c2ecf20Sopenharmony_ci	case CYSETTIMEOUT:
26628c2ecf20Sopenharmony_ci		ret_val = set_timeout(info, arg);
26638c2ecf20Sopenharmony_ci		break;
26648c2ecf20Sopenharmony_ci	case CYGETDEFTIMEOUT:
26658c2ecf20Sopenharmony_ci		ret_val = put_user(info->default_timeout,
26668c2ecf20Sopenharmony_ci				(unsigned long __user *)argp);
26678c2ecf20Sopenharmony_ci		break;
26688c2ecf20Sopenharmony_ci	case CYSETDEFTIMEOUT:
26698c2ecf20Sopenharmony_ci		info->default_timeout = arg & 0xff;
26708c2ecf20Sopenharmony_ci		break;
26718c2ecf20Sopenharmony_ci	case CYSETRFLOW:
26728c2ecf20Sopenharmony_ci		info->rflow = (int)arg;
26738c2ecf20Sopenharmony_ci		break;
26748c2ecf20Sopenharmony_ci	case CYGETRFLOW:
26758c2ecf20Sopenharmony_ci		ret_val = info->rflow;
26768c2ecf20Sopenharmony_ci		break;
26778c2ecf20Sopenharmony_ci	case CYSETRTSDTR_INV:
26788c2ecf20Sopenharmony_ci		info->rtsdtr_inv = (int)arg;
26798c2ecf20Sopenharmony_ci		break;
26808c2ecf20Sopenharmony_ci	case CYGETRTSDTR_INV:
26818c2ecf20Sopenharmony_ci		ret_val = info->rtsdtr_inv;
26828c2ecf20Sopenharmony_ci		break;
26838c2ecf20Sopenharmony_ci	case CYGETCD1400VER:
26848c2ecf20Sopenharmony_ci		ret_val = info->chip_rev;
26858c2ecf20Sopenharmony_ci		break;
26868c2ecf20Sopenharmony_ci#ifndef CONFIG_CYZ_INTR
26878c2ecf20Sopenharmony_ci	case CYZSETPOLLCYCLE:
26888c2ecf20Sopenharmony_ci		if (arg > LONG_MAX / HZ)
26898c2ecf20Sopenharmony_ci			return -ENODEV;
26908c2ecf20Sopenharmony_ci		cyz_polling_cycle = (arg * HZ) / 1000;
26918c2ecf20Sopenharmony_ci		break;
26928c2ecf20Sopenharmony_ci	case CYZGETPOLLCYCLE:
26938c2ecf20Sopenharmony_ci		ret_val = (cyz_polling_cycle * 1000) / HZ;
26948c2ecf20Sopenharmony_ci		break;
26958c2ecf20Sopenharmony_ci#endif				/* CONFIG_CYZ_INTR */
26968c2ecf20Sopenharmony_ci	case CYSETWAIT:
26978c2ecf20Sopenharmony_ci		info->port.closing_wait = (unsigned short)arg * HZ / 100;
26988c2ecf20Sopenharmony_ci		break;
26998c2ecf20Sopenharmony_ci	case CYGETWAIT:
27008c2ecf20Sopenharmony_ci		ret_val = info->port.closing_wait / (HZ / 100);
27018c2ecf20Sopenharmony_ci		break;
27028c2ecf20Sopenharmony_ci	case TIOCSERGETLSR:	/* Get line status register */
27038c2ecf20Sopenharmony_ci		ret_val = get_lsr_info(info, argp);
27048c2ecf20Sopenharmony_ci		break;
27058c2ecf20Sopenharmony_ci		/*
27068c2ecf20Sopenharmony_ci		 * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
27078c2ecf20Sopenharmony_ci		 * - mask passed in arg for lines of interest
27088c2ecf20Sopenharmony_ci		 *   (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
27098c2ecf20Sopenharmony_ci		 * Caller should use TIOCGICOUNT to see which one it was
27108c2ecf20Sopenharmony_ci		 */
27118c2ecf20Sopenharmony_ci	case TIOCMIWAIT:
27128c2ecf20Sopenharmony_ci		spin_lock_irqsave(&info->card->card_lock, flags);
27138c2ecf20Sopenharmony_ci		/* note the counters on entry */
27148c2ecf20Sopenharmony_ci		cnow = info->icount;
27158c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&info->card->card_lock, flags);
27168c2ecf20Sopenharmony_ci		ret_val = wait_event_interruptible(info->port.delta_msr_wait,
27178c2ecf20Sopenharmony_ci				cy_cflags_changed(info, arg, &cnow));
27188c2ecf20Sopenharmony_ci		break;
27198c2ecf20Sopenharmony_ci
27208c2ecf20Sopenharmony_ci		/*
27218c2ecf20Sopenharmony_ci		 * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
27228c2ecf20Sopenharmony_ci		 * Return: write counters to the user passed counter struct
27238c2ecf20Sopenharmony_ci		 * NB: both 1->0 and 0->1 transitions are counted except for
27248c2ecf20Sopenharmony_ci		 *     RI where only 0->1 is counted.
27258c2ecf20Sopenharmony_ci		 */
27268c2ecf20Sopenharmony_ci	default:
27278c2ecf20Sopenharmony_ci		ret_val = -ENOIOCTLCMD;
27288c2ecf20Sopenharmony_ci	}
27298c2ecf20Sopenharmony_ci
27308c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_OTHER
27318c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "cyc:cy_ioctl done\n");
27328c2ecf20Sopenharmony_ci#endif
27338c2ecf20Sopenharmony_ci	return ret_val;
27348c2ecf20Sopenharmony_ci}				/* cy_ioctl */
27358c2ecf20Sopenharmony_ci
27368c2ecf20Sopenharmony_cistatic int cy_get_icount(struct tty_struct *tty,
27378c2ecf20Sopenharmony_ci				struct serial_icounter_struct *sic)
27388c2ecf20Sopenharmony_ci{
27398c2ecf20Sopenharmony_ci	struct cyclades_port *info = tty->driver_data;
27408c2ecf20Sopenharmony_ci	struct cyclades_icount cnow;	/* Used to snapshot */
27418c2ecf20Sopenharmony_ci	unsigned long flags;
27428c2ecf20Sopenharmony_ci
27438c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->card->card_lock, flags);
27448c2ecf20Sopenharmony_ci	cnow = info->icount;
27458c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->card->card_lock, flags);
27468c2ecf20Sopenharmony_ci
27478c2ecf20Sopenharmony_ci	sic->cts = cnow.cts;
27488c2ecf20Sopenharmony_ci	sic->dsr = cnow.dsr;
27498c2ecf20Sopenharmony_ci	sic->rng = cnow.rng;
27508c2ecf20Sopenharmony_ci	sic->dcd = cnow.dcd;
27518c2ecf20Sopenharmony_ci	sic->rx = cnow.rx;
27528c2ecf20Sopenharmony_ci	sic->tx = cnow.tx;
27538c2ecf20Sopenharmony_ci	sic->frame = cnow.frame;
27548c2ecf20Sopenharmony_ci	sic->overrun = cnow.overrun;
27558c2ecf20Sopenharmony_ci	sic->parity = cnow.parity;
27568c2ecf20Sopenharmony_ci	sic->brk = cnow.brk;
27578c2ecf20Sopenharmony_ci	sic->buf_overrun = cnow.buf_overrun;
27588c2ecf20Sopenharmony_ci	return 0;
27598c2ecf20Sopenharmony_ci}
27608c2ecf20Sopenharmony_ci
27618c2ecf20Sopenharmony_ci/*
27628c2ecf20Sopenharmony_ci * This routine allows the tty driver to be notified when
27638c2ecf20Sopenharmony_ci * device's termios settings have changed.  Note that a
27648c2ecf20Sopenharmony_ci * well-designed tty driver should be prepared to accept the case
27658c2ecf20Sopenharmony_ci * where old == NULL, and try to do something rational.
27668c2ecf20Sopenharmony_ci */
27678c2ecf20Sopenharmony_cistatic void cy_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
27688c2ecf20Sopenharmony_ci{
27698c2ecf20Sopenharmony_ci	struct cyclades_port *info = tty->driver_data;
27708c2ecf20Sopenharmony_ci
27718c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_OTHER
27728c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "cyc:cy_set_termios ttyC%d\n", info->line);
27738c2ecf20Sopenharmony_ci#endif
27748c2ecf20Sopenharmony_ci
27758c2ecf20Sopenharmony_ci	cy_set_line_char(info, tty);
27768c2ecf20Sopenharmony_ci
27778c2ecf20Sopenharmony_ci	if ((old_termios->c_cflag & CRTSCTS) && !C_CRTSCTS(tty)) {
27788c2ecf20Sopenharmony_ci		tty->hw_stopped = 0;
27798c2ecf20Sopenharmony_ci		cy_start(tty);
27808c2ecf20Sopenharmony_ci	}
27818c2ecf20Sopenharmony_ci#if 0
27828c2ecf20Sopenharmony_ci	/*
27838c2ecf20Sopenharmony_ci	 * No need to wake up processes in open wait, since they
27848c2ecf20Sopenharmony_ci	 * sample the CLOCAL flag once, and don't recheck it.
27858c2ecf20Sopenharmony_ci	 * XXX  It's not clear whether the current behavior is correct
27868c2ecf20Sopenharmony_ci	 * or not.  Hence, this may change.....
27878c2ecf20Sopenharmony_ci	 */
27888c2ecf20Sopenharmony_ci	if (!(old_termios->c_cflag & CLOCAL) && C_CLOCAL(tty))
27898c2ecf20Sopenharmony_ci		wake_up_interruptible(&info->port.open_wait);
27908c2ecf20Sopenharmony_ci#endif
27918c2ecf20Sopenharmony_ci}				/* cy_set_termios */
27928c2ecf20Sopenharmony_ci
27938c2ecf20Sopenharmony_ci/* This function is used to send a high-priority XON/XOFF character to
27948c2ecf20Sopenharmony_ci   the device.
27958c2ecf20Sopenharmony_ci*/
27968c2ecf20Sopenharmony_cistatic void cy_send_xchar(struct tty_struct *tty, char ch)
27978c2ecf20Sopenharmony_ci{
27988c2ecf20Sopenharmony_ci	struct cyclades_port *info = tty->driver_data;
27998c2ecf20Sopenharmony_ci	struct cyclades_card *card;
28008c2ecf20Sopenharmony_ci	int channel;
28018c2ecf20Sopenharmony_ci
28028c2ecf20Sopenharmony_ci	if (serial_paranoia_check(info, tty->name, "cy_send_xchar"))
28038c2ecf20Sopenharmony_ci		return;
28048c2ecf20Sopenharmony_ci
28058c2ecf20Sopenharmony_ci	info->x_char = ch;
28068c2ecf20Sopenharmony_ci
28078c2ecf20Sopenharmony_ci	if (ch)
28088c2ecf20Sopenharmony_ci		cy_start(tty);
28098c2ecf20Sopenharmony_ci
28108c2ecf20Sopenharmony_ci	card = info->card;
28118c2ecf20Sopenharmony_ci	channel = info->line - card->first_line;
28128c2ecf20Sopenharmony_ci
28138c2ecf20Sopenharmony_ci	if (cy_is_Z(card)) {
28148c2ecf20Sopenharmony_ci		if (ch == STOP_CHAR(tty))
28158c2ecf20Sopenharmony_ci			cyz_issue_cmd(card, channel, C_CM_SENDXOFF, 0L);
28168c2ecf20Sopenharmony_ci		else if (ch == START_CHAR(tty))
28178c2ecf20Sopenharmony_ci			cyz_issue_cmd(card, channel, C_CM_SENDXON, 0L);
28188c2ecf20Sopenharmony_ci	}
28198c2ecf20Sopenharmony_ci}
28208c2ecf20Sopenharmony_ci
28218c2ecf20Sopenharmony_ci/* This routine is called by the upper-layer tty layer to signal
28228c2ecf20Sopenharmony_ci   that incoming characters should be throttled because the input
28238c2ecf20Sopenharmony_ci   buffers are close to full.
28248c2ecf20Sopenharmony_ci */
28258c2ecf20Sopenharmony_cistatic void cy_throttle(struct tty_struct *tty)
28268c2ecf20Sopenharmony_ci{
28278c2ecf20Sopenharmony_ci	struct cyclades_port *info = tty->driver_data;
28288c2ecf20Sopenharmony_ci	struct cyclades_card *card;
28298c2ecf20Sopenharmony_ci	unsigned long flags;
28308c2ecf20Sopenharmony_ci
28318c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_THROTTLE
28328c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "cyc:throttle %s ...ttyC%d\n", tty_name(tty),
28338c2ecf20Sopenharmony_ci			 info->line);
28348c2ecf20Sopenharmony_ci#endif
28358c2ecf20Sopenharmony_ci
28368c2ecf20Sopenharmony_ci	if (serial_paranoia_check(info, tty->name, "cy_throttle"))
28378c2ecf20Sopenharmony_ci		return;
28388c2ecf20Sopenharmony_ci
28398c2ecf20Sopenharmony_ci	card = info->card;
28408c2ecf20Sopenharmony_ci
28418c2ecf20Sopenharmony_ci	if (I_IXOFF(tty)) {
28428c2ecf20Sopenharmony_ci		if (!cy_is_Z(card))
28438c2ecf20Sopenharmony_ci			cy_send_xchar(tty, STOP_CHAR(tty));
28448c2ecf20Sopenharmony_ci		else
28458c2ecf20Sopenharmony_ci			info->throttle = 1;
28468c2ecf20Sopenharmony_ci	}
28478c2ecf20Sopenharmony_ci
28488c2ecf20Sopenharmony_ci	if (C_CRTSCTS(tty)) {
28498c2ecf20Sopenharmony_ci		if (!cy_is_Z(card)) {
28508c2ecf20Sopenharmony_ci			spin_lock_irqsave(&card->card_lock, flags);
28518c2ecf20Sopenharmony_ci			cyy_change_rts_dtr(info, 0, TIOCM_RTS);
28528c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&card->card_lock, flags);
28538c2ecf20Sopenharmony_ci		} else {
28548c2ecf20Sopenharmony_ci			info->throttle = 1;
28558c2ecf20Sopenharmony_ci		}
28568c2ecf20Sopenharmony_ci	}
28578c2ecf20Sopenharmony_ci}				/* cy_throttle */
28588c2ecf20Sopenharmony_ci
28598c2ecf20Sopenharmony_ci/*
28608c2ecf20Sopenharmony_ci * This routine notifies the tty driver that it should signal
28618c2ecf20Sopenharmony_ci * that characters can now be sent to the tty without fear of
28628c2ecf20Sopenharmony_ci * overrunning the input buffers of the line disciplines.
28638c2ecf20Sopenharmony_ci */
28648c2ecf20Sopenharmony_cistatic void cy_unthrottle(struct tty_struct *tty)
28658c2ecf20Sopenharmony_ci{
28668c2ecf20Sopenharmony_ci	struct cyclades_port *info = tty->driver_data;
28678c2ecf20Sopenharmony_ci	struct cyclades_card *card;
28688c2ecf20Sopenharmony_ci	unsigned long flags;
28698c2ecf20Sopenharmony_ci
28708c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_THROTTLE
28718c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "cyc:unthrottle %s ...ttyC%d\n",
28728c2ecf20Sopenharmony_ci		tty_name(tty), info->line);
28738c2ecf20Sopenharmony_ci#endif
28748c2ecf20Sopenharmony_ci
28758c2ecf20Sopenharmony_ci	if (serial_paranoia_check(info, tty->name, "cy_unthrottle"))
28768c2ecf20Sopenharmony_ci		return;
28778c2ecf20Sopenharmony_ci
28788c2ecf20Sopenharmony_ci	if (I_IXOFF(tty)) {
28798c2ecf20Sopenharmony_ci		if (info->x_char)
28808c2ecf20Sopenharmony_ci			info->x_char = 0;
28818c2ecf20Sopenharmony_ci		else
28828c2ecf20Sopenharmony_ci			cy_send_xchar(tty, START_CHAR(tty));
28838c2ecf20Sopenharmony_ci	}
28848c2ecf20Sopenharmony_ci
28858c2ecf20Sopenharmony_ci	if (C_CRTSCTS(tty)) {
28868c2ecf20Sopenharmony_ci		card = info->card;
28878c2ecf20Sopenharmony_ci		if (!cy_is_Z(card)) {
28888c2ecf20Sopenharmony_ci			spin_lock_irqsave(&card->card_lock, flags);
28898c2ecf20Sopenharmony_ci			cyy_change_rts_dtr(info, TIOCM_RTS, 0);
28908c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&card->card_lock, flags);
28918c2ecf20Sopenharmony_ci		} else {
28928c2ecf20Sopenharmony_ci			info->throttle = 0;
28938c2ecf20Sopenharmony_ci		}
28948c2ecf20Sopenharmony_ci	}
28958c2ecf20Sopenharmony_ci}				/* cy_unthrottle */
28968c2ecf20Sopenharmony_ci
28978c2ecf20Sopenharmony_ci/* cy_start and cy_stop provide software output flow control as a
28988c2ecf20Sopenharmony_ci   function of XON/XOFF, software CTS, and other such stuff.
28998c2ecf20Sopenharmony_ci*/
29008c2ecf20Sopenharmony_cistatic void cy_stop(struct tty_struct *tty)
29018c2ecf20Sopenharmony_ci{
29028c2ecf20Sopenharmony_ci	struct cyclades_card *cinfo;
29038c2ecf20Sopenharmony_ci	struct cyclades_port *info = tty->driver_data;
29048c2ecf20Sopenharmony_ci	int channel;
29058c2ecf20Sopenharmony_ci	unsigned long flags;
29068c2ecf20Sopenharmony_ci
29078c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_OTHER
29088c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "cyc:cy_stop ttyC%d\n", info->line);
29098c2ecf20Sopenharmony_ci#endif
29108c2ecf20Sopenharmony_ci
29118c2ecf20Sopenharmony_ci	if (serial_paranoia_check(info, tty->name, "cy_stop"))
29128c2ecf20Sopenharmony_ci		return;
29138c2ecf20Sopenharmony_ci
29148c2ecf20Sopenharmony_ci	cinfo = info->card;
29158c2ecf20Sopenharmony_ci	channel = info->line - cinfo->first_line;
29168c2ecf20Sopenharmony_ci	if (!cy_is_Z(cinfo)) {
29178c2ecf20Sopenharmony_ci		spin_lock_irqsave(&cinfo->card_lock, flags);
29188c2ecf20Sopenharmony_ci		cyy_writeb(info, CyCAR, channel & 0x03);
29198c2ecf20Sopenharmony_ci		cyy_writeb(info, CySRER, cyy_readb(info, CySRER) & ~CyTxRdy);
29208c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&cinfo->card_lock, flags);
29218c2ecf20Sopenharmony_ci	}
29228c2ecf20Sopenharmony_ci}				/* cy_stop */
29238c2ecf20Sopenharmony_ci
29248c2ecf20Sopenharmony_cistatic void cy_start(struct tty_struct *tty)
29258c2ecf20Sopenharmony_ci{
29268c2ecf20Sopenharmony_ci	struct cyclades_card *cinfo;
29278c2ecf20Sopenharmony_ci	struct cyclades_port *info = tty->driver_data;
29288c2ecf20Sopenharmony_ci	int channel;
29298c2ecf20Sopenharmony_ci	unsigned long flags;
29308c2ecf20Sopenharmony_ci
29318c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_OTHER
29328c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "cyc:cy_start ttyC%d\n", info->line);
29338c2ecf20Sopenharmony_ci#endif
29348c2ecf20Sopenharmony_ci
29358c2ecf20Sopenharmony_ci	if (serial_paranoia_check(info, tty->name, "cy_start"))
29368c2ecf20Sopenharmony_ci		return;
29378c2ecf20Sopenharmony_ci
29388c2ecf20Sopenharmony_ci	cinfo = info->card;
29398c2ecf20Sopenharmony_ci	channel = info->line - cinfo->first_line;
29408c2ecf20Sopenharmony_ci	if (!cy_is_Z(cinfo)) {
29418c2ecf20Sopenharmony_ci		spin_lock_irqsave(&cinfo->card_lock, flags);
29428c2ecf20Sopenharmony_ci		cyy_writeb(info, CyCAR, channel & 0x03);
29438c2ecf20Sopenharmony_ci		cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyTxRdy);
29448c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&cinfo->card_lock, flags);
29458c2ecf20Sopenharmony_ci	}
29468c2ecf20Sopenharmony_ci}				/* cy_start */
29478c2ecf20Sopenharmony_ci
29488c2ecf20Sopenharmony_ci/*
29498c2ecf20Sopenharmony_ci * cy_hangup() --- called by tty_hangup() when a hangup is signaled.
29508c2ecf20Sopenharmony_ci */
29518c2ecf20Sopenharmony_cistatic void cy_hangup(struct tty_struct *tty)
29528c2ecf20Sopenharmony_ci{
29538c2ecf20Sopenharmony_ci	struct cyclades_port *info = tty->driver_data;
29548c2ecf20Sopenharmony_ci
29558c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_OTHER
29568c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "cyc:cy_hangup ttyC%d\n", info->line);
29578c2ecf20Sopenharmony_ci#endif
29588c2ecf20Sopenharmony_ci
29598c2ecf20Sopenharmony_ci	if (serial_paranoia_check(info, tty->name, "cy_hangup"))
29608c2ecf20Sopenharmony_ci		return;
29618c2ecf20Sopenharmony_ci
29628c2ecf20Sopenharmony_ci	cy_flush_buffer(tty);
29638c2ecf20Sopenharmony_ci	cy_shutdown(info, tty);
29648c2ecf20Sopenharmony_ci	tty_port_hangup(&info->port);
29658c2ecf20Sopenharmony_ci}				/* cy_hangup */
29668c2ecf20Sopenharmony_ci
29678c2ecf20Sopenharmony_cistatic int cyy_carrier_raised(struct tty_port *port)
29688c2ecf20Sopenharmony_ci{
29698c2ecf20Sopenharmony_ci	struct cyclades_port *info = container_of(port, struct cyclades_port,
29708c2ecf20Sopenharmony_ci			port);
29718c2ecf20Sopenharmony_ci	struct cyclades_card *cinfo = info->card;
29728c2ecf20Sopenharmony_ci	unsigned long flags;
29738c2ecf20Sopenharmony_ci	int channel = info->line - cinfo->first_line;
29748c2ecf20Sopenharmony_ci	u32 cd;
29758c2ecf20Sopenharmony_ci
29768c2ecf20Sopenharmony_ci	spin_lock_irqsave(&cinfo->card_lock, flags);
29778c2ecf20Sopenharmony_ci	cyy_writeb(info, CyCAR, channel & 0x03);
29788c2ecf20Sopenharmony_ci	cd = cyy_readb(info, CyMSVR1) & CyDCD;
29798c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&cinfo->card_lock, flags);
29808c2ecf20Sopenharmony_ci
29818c2ecf20Sopenharmony_ci	return cd;
29828c2ecf20Sopenharmony_ci}
29838c2ecf20Sopenharmony_ci
29848c2ecf20Sopenharmony_cistatic void cyy_dtr_rts(struct tty_port *port, int raise)
29858c2ecf20Sopenharmony_ci{
29868c2ecf20Sopenharmony_ci	struct cyclades_port *info = container_of(port, struct cyclades_port,
29878c2ecf20Sopenharmony_ci			port);
29888c2ecf20Sopenharmony_ci	struct cyclades_card *cinfo = info->card;
29898c2ecf20Sopenharmony_ci	unsigned long flags;
29908c2ecf20Sopenharmony_ci
29918c2ecf20Sopenharmony_ci	spin_lock_irqsave(&cinfo->card_lock, flags);
29928c2ecf20Sopenharmony_ci	cyy_change_rts_dtr(info, raise ? TIOCM_RTS | TIOCM_DTR : 0,
29938c2ecf20Sopenharmony_ci			raise ? 0 : TIOCM_RTS | TIOCM_DTR);
29948c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&cinfo->card_lock, flags);
29958c2ecf20Sopenharmony_ci}
29968c2ecf20Sopenharmony_ci
29978c2ecf20Sopenharmony_cistatic int cyz_carrier_raised(struct tty_port *port)
29988c2ecf20Sopenharmony_ci{
29998c2ecf20Sopenharmony_ci	struct cyclades_port *info = container_of(port, struct cyclades_port,
30008c2ecf20Sopenharmony_ci			port);
30018c2ecf20Sopenharmony_ci
30028c2ecf20Sopenharmony_ci	return readl(&info->u.cyz.ch_ctrl->rs_status) & C_RS_DCD;
30038c2ecf20Sopenharmony_ci}
30048c2ecf20Sopenharmony_ci
30058c2ecf20Sopenharmony_cistatic void cyz_dtr_rts(struct tty_port *port, int raise)
30068c2ecf20Sopenharmony_ci{
30078c2ecf20Sopenharmony_ci	struct cyclades_port *info = container_of(port, struct cyclades_port,
30088c2ecf20Sopenharmony_ci			port);
30098c2ecf20Sopenharmony_ci	struct cyclades_card *cinfo = info->card;
30108c2ecf20Sopenharmony_ci	struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl;
30118c2ecf20Sopenharmony_ci	int ret, channel = info->line - cinfo->first_line;
30128c2ecf20Sopenharmony_ci	u32 rs;
30138c2ecf20Sopenharmony_ci
30148c2ecf20Sopenharmony_ci	rs = readl(&ch_ctrl->rs_control);
30158c2ecf20Sopenharmony_ci	if (raise)
30168c2ecf20Sopenharmony_ci		rs |= C_RS_RTS | C_RS_DTR;
30178c2ecf20Sopenharmony_ci	else
30188c2ecf20Sopenharmony_ci		rs &= ~(C_RS_RTS | C_RS_DTR);
30198c2ecf20Sopenharmony_ci	cy_writel(&ch_ctrl->rs_control, rs);
30208c2ecf20Sopenharmony_ci	ret = cyz_issue_cmd(cinfo, channel, C_CM_IOCTLM, 0L);
30218c2ecf20Sopenharmony_ci	if (ret != 0)
30228c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: retval on ttyC%d was %x\n",
30238c2ecf20Sopenharmony_ci				__func__, info->line, ret);
30248c2ecf20Sopenharmony_ci#ifdef CY_DEBUG_DTR
30258c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "%s: raising Z DTR\n", __func__);
30268c2ecf20Sopenharmony_ci#endif
30278c2ecf20Sopenharmony_ci}
30288c2ecf20Sopenharmony_ci
30298c2ecf20Sopenharmony_cistatic const struct tty_port_operations cyy_port_ops = {
30308c2ecf20Sopenharmony_ci	.carrier_raised = cyy_carrier_raised,
30318c2ecf20Sopenharmony_ci	.dtr_rts = cyy_dtr_rts,
30328c2ecf20Sopenharmony_ci	.shutdown = cy_do_close,
30338c2ecf20Sopenharmony_ci};
30348c2ecf20Sopenharmony_ci
30358c2ecf20Sopenharmony_cistatic const struct tty_port_operations cyz_port_ops = {
30368c2ecf20Sopenharmony_ci	.carrier_raised = cyz_carrier_raised,
30378c2ecf20Sopenharmony_ci	.dtr_rts = cyz_dtr_rts,
30388c2ecf20Sopenharmony_ci	.shutdown = cy_do_close,
30398c2ecf20Sopenharmony_ci};
30408c2ecf20Sopenharmony_ci
30418c2ecf20Sopenharmony_ci/*
30428c2ecf20Sopenharmony_ci * ---------------------------------------------------------------------
30438c2ecf20Sopenharmony_ci * cy_init() and friends
30448c2ecf20Sopenharmony_ci *
30458c2ecf20Sopenharmony_ci * cy_init() is called at boot-time to initialize the serial driver.
30468c2ecf20Sopenharmony_ci * ---------------------------------------------------------------------
30478c2ecf20Sopenharmony_ci */
30488c2ecf20Sopenharmony_ci
30498c2ecf20Sopenharmony_cistatic int cy_init_card(struct cyclades_card *cinfo)
30508c2ecf20Sopenharmony_ci{
30518c2ecf20Sopenharmony_ci	struct cyclades_port *info;
30528c2ecf20Sopenharmony_ci	unsigned int channel, port;
30538c2ecf20Sopenharmony_ci
30548c2ecf20Sopenharmony_ci	spin_lock_init(&cinfo->card_lock);
30558c2ecf20Sopenharmony_ci	cinfo->intr_enabled = 0;
30568c2ecf20Sopenharmony_ci
30578c2ecf20Sopenharmony_ci	cinfo->ports = kcalloc(cinfo->nports, sizeof(*cinfo->ports),
30588c2ecf20Sopenharmony_ci			GFP_KERNEL);
30598c2ecf20Sopenharmony_ci	if (cinfo->ports == NULL) {
30608c2ecf20Sopenharmony_ci		printk(KERN_ERR "Cyclades: cannot allocate ports\n");
30618c2ecf20Sopenharmony_ci		return -ENOMEM;
30628c2ecf20Sopenharmony_ci	}
30638c2ecf20Sopenharmony_ci
30648c2ecf20Sopenharmony_ci	for (channel = 0, port = cinfo->first_line; channel < cinfo->nports;
30658c2ecf20Sopenharmony_ci			channel++, port++) {
30668c2ecf20Sopenharmony_ci		info = &cinfo->ports[channel];
30678c2ecf20Sopenharmony_ci		tty_port_init(&info->port);
30688c2ecf20Sopenharmony_ci		info->magic = CYCLADES_MAGIC;
30698c2ecf20Sopenharmony_ci		info->card = cinfo;
30708c2ecf20Sopenharmony_ci		info->line = port;
30718c2ecf20Sopenharmony_ci
30728c2ecf20Sopenharmony_ci		info->port.closing_wait = CLOSING_WAIT_DELAY;
30738c2ecf20Sopenharmony_ci		info->port.close_delay = 5 * HZ / 10;
30748c2ecf20Sopenharmony_ci		init_completion(&info->shutdown_wait);
30758c2ecf20Sopenharmony_ci
30768c2ecf20Sopenharmony_ci		if (cy_is_Z(cinfo)) {
30778c2ecf20Sopenharmony_ci			struct FIRM_ID *firm_id = cinfo->base_addr + ID_ADDRESS;
30788c2ecf20Sopenharmony_ci			struct ZFW_CTRL *zfw_ctrl;
30798c2ecf20Sopenharmony_ci
30808c2ecf20Sopenharmony_ci			info->port.ops = &cyz_port_ops;
30818c2ecf20Sopenharmony_ci			info->type = PORT_STARTECH;
30828c2ecf20Sopenharmony_ci
30838c2ecf20Sopenharmony_ci			zfw_ctrl = cinfo->base_addr +
30848c2ecf20Sopenharmony_ci				(readl(&firm_id->zfwctrl_addr) & 0xfffff);
30858c2ecf20Sopenharmony_ci			info->u.cyz.ch_ctrl = &zfw_ctrl->ch_ctrl[channel];
30868c2ecf20Sopenharmony_ci			info->u.cyz.buf_ctrl = &zfw_ctrl->buf_ctrl[channel];
30878c2ecf20Sopenharmony_ci
30888c2ecf20Sopenharmony_ci			if (cinfo->hw_ver == ZO_V1)
30898c2ecf20Sopenharmony_ci				info->xmit_fifo_size = CYZ_FIFO_SIZE;
30908c2ecf20Sopenharmony_ci			else
30918c2ecf20Sopenharmony_ci				info->xmit_fifo_size = 4 * CYZ_FIFO_SIZE;
30928c2ecf20Sopenharmony_ci#ifdef CONFIG_CYZ_INTR
30938c2ecf20Sopenharmony_ci			timer_setup(&info->rx_full_timer, cyz_rx_restart, 0);
30948c2ecf20Sopenharmony_ci#endif
30958c2ecf20Sopenharmony_ci		} else {
30968c2ecf20Sopenharmony_ci			unsigned short chip_number;
30978c2ecf20Sopenharmony_ci			int index = cinfo->bus_index;
30988c2ecf20Sopenharmony_ci
30998c2ecf20Sopenharmony_ci			info->port.ops = &cyy_port_ops;
31008c2ecf20Sopenharmony_ci			info->type = PORT_CIRRUS;
31018c2ecf20Sopenharmony_ci			info->xmit_fifo_size = CyMAX_CHAR_FIFO;
31028c2ecf20Sopenharmony_ci			info->cor1 = CyPARITY_NONE | Cy_1_STOP | Cy_8_BITS;
31038c2ecf20Sopenharmony_ci			info->cor2 = CyETC;
31048c2ecf20Sopenharmony_ci			info->cor3 = 0x08;	/* _very_ small rcv threshold */
31058c2ecf20Sopenharmony_ci
31068c2ecf20Sopenharmony_ci			chip_number = channel / CyPORTS_PER_CHIP;
31078c2ecf20Sopenharmony_ci			info->u.cyy.base_addr = cinfo->base_addr +
31088c2ecf20Sopenharmony_ci				(cy_chip_offset[chip_number] << index);
31098c2ecf20Sopenharmony_ci			info->chip_rev = cyy_readb(info, CyGFRCR);
31108c2ecf20Sopenharmony_ci
31118c2ecf20Sopenharmony_ci			if (info->chip_rev >= CD1400_REV_J) {
31128c2ecf20Sopenharmony_ci				/* It is a CD1400 rev. J or later */
31138c2ecf20Sopenharmony_ci				info->tbpr = baud_bpr_60[13];	/* Tx BPR */
31148c2ecf20Sopenharmony_ci				info->tco = baud_co_60[13];	/* Tx CO */
31158c2ecf20Sopenharmony_ci				info->rbpr = baud_bpr_60[13];	/* Rx BPR */
31168c2ecf20Sopenharmony_ci				info->rco = baud_co_60[13];	/* Rx CO */
31178c2ecf20Sopenharmony_ci				info->rtsdtr_inv = 1;
31188c2ecf20Sopenharmony_ci			} else {
31198c2ecf20Sopenharmony_ci				info->tbpr = baud_bpr_25[13];	/* Tx BPR */
31208c2ecf20Sopenharmony_ci				info->tco = baud_co_25[13];	/* Tx CO */
31218c2ecf20Sopenharmony_ci				info->rbpr = baud_bpr_25[13];	/* Rx BPR */
31228c2ecf20Sopenharmony_ci				info->rco = baud_co_25[13];	/* Rx CO */
31238c2ecf20Sopenharmony_ci				info->rtsdtr_inv = 0;
31248c2ecf20Sopenharmony_ci			}
31258c2ecf20Sopenharmony_ci			info->read_status_mask = CyTIMEOUT | CySPECHAR |
31268c2ecf20Sopenharmony_ci				CyBREAK | CyPARITY | CyFRAME | CyOVERRUN;
31278c2ecf20Sopenharmony_ci		}
31288c2ecf20Sopenharmony_ci
31298c2ecf20Sopenharmony_ci	}
31308c2ecf20Sopenharmony_ci
31318c2ecf20Sopenharmony_ci#ifndef CONFIG_CYZ_INTR
31328c2ecf20Sopenharmony_ci	if (cy_is_Z(cinfo) && !timer_pending(&cyz_timerlist)) {
31338c2ecf20Sopenharmony_ci		mod_timer(&cyz_timerlist, jiffies + 1);
31348c2ecf20Sopenharmony_ci#ifdef CY_PCI_DEBUG
31358c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "Cyclades-Z polling initialized\n");
31368c2ecf20Sopenharmony_ci#endif
31378c2ecf20Sopenharmony_ci	}
31388c2ecf20Sopenharmony_ci#endif
31398c2ecf20Sopenharmony_ci	return 0;
31408c2ecf20Sopenharmony_ci}
31418c2ecf20Sopenharmony_ci
31428c2ecf20Sopenharmony_ci/* initialize chips on Cyclom-Y card -- return number of valid
31438c2ecf20Sopenharmony_ci   chips (which is number of ports/4) */
31448c2ecf20Sopenharmony_cistatic unsigned short cyy_init_card(void __iomem *true_base_addr,
31458c2ecf20Sopenharmony_ci		int index)
31468c2ecf20Sopenharmony_ci{
31478c2ecf20Sopenharmony_ci	unsigned int chip_number;
31488c2ecf20Sopenharmony_ci	void __iomem *base_addr;
31498c2ecf20Sopenharmony_ci
31508c2ecf20Sopenharmony_ci	cy_writeb(true_base_addr + (Cy_HwReset << index), 0);
31518c2ecf20Sopenharmony_ci	/* Cy_HwReset is 0x1400 */
31528c2ecf20Sopenharmony_ci	cy_writeb(true_base_addr + (Cy_ClrIntr << index), 0);
31538c2ecf20Sopenharmony_ci	/* Cy_ClrIntr is 0x1800 */
31548c2ecf20Sopenharmony_ci	udelay(500L);
31558c2ecf20Sopenharmony_ci
31568c2ecf20Sopenharmony_ci	for (chip_number = 0; chip_number < CyMAX_CHIPS_PER_CARD;
31578c2ecf20Sopenharmony_ci							chip_number++) {
31588c2ecf20Sopenharmony_ci		base_addr =
31598c2ecf20Sopenharmony_ci		    true_base_addr + (cy_chip_offset[chip_number] << index);
31608c2ecf20Sopenharmony_ci		mdelay(1);
31618c2ecf20Sopenharmony_ci		if (readb(base_addr + (CyCCR << index)) != 0x00) {
31628c2ecf20Sopenharmony_ci			/*************
31638c2ecf20Sopenharmony_ci			printk(" chip #%d at %#6lx is never idle (CCR != 0)\n",
31648c2ecf20Sopenharmony_ci			chip_number, (unsigned long)base_addr);
31658c2ecf20Sopenharmony_ci			*************/
31668c2ecf20Sopenharmony_ci			return chip_number;
31678c2ecf20Sopenharmony_ci		}
31688c2ecf20Sopenharmony_ci
31698c2ecf20Sopenharmony_ci		cy_writeb(base_addr + (CyGFRCR << index), 0);
31708c2ecf20Sopenharmony_ci		udelay(10L);
31718c2ecf20Sopenharmony_ci
31728c2ecf20Sopenharmony_ci		/* The Cyclom-16Y does not decode address bit 9 and therefore
31738c2ecf20Sopenharmony_ci		   cannot distinguish between references to chip 0 and a non-
31748c2ecf20Sopenharmony_ci		   existent chip 4.  If the preceding clearing of the supposed
31758c2ecf20Sopenharmony_ci		   chip 4 GFRCR register appears at chip 0, there is no chip 4
31768c2ecf20Sopenharmony_ci		   and this must be a Cyclom-16Y, not a Cyclom-32Ye.
31778c2ecf20Sopenharmony_ci		 */
31788c2ecf20Sopenharmony_ci		if (chip_number == 4 && readb(true_base_addr +
31798c2ecf20Sopenharmony_ci				(cy_chip_offset[0] << index) +
31808c2ecf20Sopenharmony_ci				(CyGFRCR << index)) == 0) {
31818c2ecf20Sopenharmony_ci			return chip_number;
31828c2ecf20Sopenharmony_ci		}
31838c2ecf20Sopenharmony_ci
31848c2ecf20Sopenharmony_ci		cy_writeb(base_addr + (CyCCR << index), CyCHIP_RESET);
31858c2ecf20Sopenharmony_ci		mdelay(1);
31868c2ecf20Sopenharmony_ci
31878c2ecf20Sopenharmony_ci		if (readb(base_addr + (CyGFRCR << index)) == 0x00) {
31888c2ecf20Sopenharmony_ci			/*
31898c2ecf20Sopenharmony_ci			   printk(" chip #%d at %#6lx is not responding ",
31908c2ecf20Sopenharmony_ci			   chip_number, (unsigned long)base_addr);
31918c2ecf20Sopenharmony_ci			   printk("(GFRCR stayed 0)\n",
31928c2ecf20Sopenharmony_ci			 */
31938c2ecf20Sopenharmony_ci			return chip_number;
31948c2ecf20Sopenharmony_ci		}
31958c2ecf20Sopenharmony_ci		if ((0xf0 & (readb(base_addr + (CyGFRCR << index)))) !=
31968c2ecf20Sopenharmony_ci				0x40) {
31978c2ecf20Sopenharmony_ci			/*
31988c2ecf20Sopenharmony_ci			printk(" chip #%d at %#6lx is not valid (GFRCR == "
31998c2ecf20Sopenharmony_ci					"%#2x)\n",
32008c2ecf20Sopenharmony_ci					chip_number, (unsigned long)base_addr,
32018c2ecf20Sopenharmony_ci					base_addr[CyGFRCR<<index]);
32028c2ecf20Sopenharmony_ci			 */
32038c2ecf20Sopenharmony_ci			return chip_number;
32048c2ecf20Sopenharmony_ci		}
32058c2ecf20Sopenharmony_ci		cy_writeb(base_addr + (CyGCR << index), CyCH0_SERIAL);
32068c2ecf20Sopenharmony_ci		if (readb(base_addr + (CyGFRCR << index)) >= CD1400_REV_J) {
32078c2ecf20Sopenharmony_ci			/* It is a CD1400 rev. J or later */
32088c2ecf20Sopenharmony_ci			/* Impossible to reach 5ms with this chip.
32098c2ecf20Sopenharmony_ci			   Changed to 2ms instead (f = 500 Hz). */
32108c2ecf20Sopenharmony_ci			cy_writeb(base_addr + (CyPPR << index), CyCLOCK_60_2MS);
32118c2ecf20Sopenharmony_ci		} else {
32128c2ecf20Sopenharmony_ci			/* f = 200 Hz */
32138c2ecf20Sopenharmony_ci			cy_writeb(base_addr + (CyPPR << index), CyCLOCK_25_5MS);
32148c2ecf20Sopenharmony_ci		}
32158c2ecf20Sopenharmony_ci
32168c2ecf20Sopenharmony_ci		/*
32178c2ecf20Sopenharmony_ci		   printk(" chip #%d at %#6lx is rev 0x%2x\n",
32188c2ecf20Sopenharmony_ci		   chip_number, (unsigned long)base_addr,
32198c2ecf20Sopenharmony_ci		   readb(base_addr+(CyGFRCR<<index)));
32208c2ecf20Sopenharmony_ci		 */
32218c2ecf20Sopenharmony_ci	}
32228c2ecf20Sopenharmony_ci	return chip_number;
32238c2ecf20Sopenharmony_ci}				/* cyy_init_card */
32248c2ecf20Sopenharmony_ci
32258c2ecf20Sopenharmony_ci/*
32268c2ecf20Sopenharmony_ci * ---------------------------------------------------------------------
32278c2ecf20Sopenharmony_ci * cy_detect_isa() - Probe for Cyclom-Y/ISA boards.
32288c2ecf20Sopenharmony_ci * sets global variables and return the number of ISA boards found.
32298c2ecf20Sopenharmony_ci * ---------------------------------------------------------------------
32308c2ecf20Sopenharmony_ci */
32318c2ecf20Sopenharmony_cistatic int __init cy_detect_isa(void)
32328c2ecf20Sopenharmony_ci{
32338c2ecf20Sopenharmony_ci#ifdef CONFIG_ISA
32348c2ecf20Sopenharmony_ci	struct cyclades_card *card;
32358c2ecf20Sopenharmony_ci	unsigned short cy_isa_irq, nboard;
32368c2ecf20Sopenharmony_ci	void __iomem *cy_isa_address;
32378c2ecf20Sopenharmony_ci	unsigned short i, j, k, cy_isa_nchan;
32388c2ecf20Sopenharmony_ci	int isparam = 0;
32398c2ecf20Sopenharmony_ci
32408c2ecf20Sopenharmony_ci	nboard = 0;
32418c2ecf20Sopenharmony_ci
32428c2ecf20Sopenharmony_ci	/* Check for module parameters */
32438c2ecf20Sopenharmony_ci	for (i = 0; i < NR_CARDS; i++) {
32448c2ecf20Sopenharmony_ci		if (maddr[i] || i) {
32458c2ecf20Sopenharmony_ci			isparam = 1;
32468c2ecf20Sopenharmony_ci			cy_isa_addresses[i] = maddr[i];
32478c2ecf20Sopenharmony_ci		}
32488c2ecf20Sopenharmony_ci		if (!maddr[i])
32498c2ecf20Sopenharmony_ci			break;
32508c2ecf20Sopenharmony_ci	}
32518c2ecf20Sopenharmony_ci
32528c2ecf20Sopenharmony_ci	/* scan the address table probing for Cyclom-Y/ISA boards */
32538c2ecf20Sopenharmony_ci	for (i = 0; i < NR_ISA_ADDRS; i++) {
32548c2ecf20Sopenharmony_ci		unsigned int isa_address = cy_isa_addresses[i];
32558c2ecf20Sopenharmony_ci		if (isa_address == 0x0000)
32568c2ecf20Sopenharmony_ci			return nboard;
32578c2ecf20Sopenharmony_ci
32588c2ecf20Sopenharmony_ci		/* probe for CD1400... */
32598c2ecf20Sopenharmony_ci		cy_isa_address = ioremap(isa_address, CyISA_Ywin);
32608c2ecf20Sopenharmony_ci		if (cy_isa_address == NULL) {
32618c2ecf20Sopenharmony_ci			printk(KERN_ERR "Cyclom-Y/ISA: can't remap base "
32628c2ecf20Sopenharmony_ci					"address\n");
32638c2ecf20Sopenharmony_ci			continue;
32648c2ecf20Sopenharmony_ci		}
32658c2ecf20Sopenharmony_ci		cy_isa_nchan = CyPORTS_PER_CHIP *
32668c2ecf20Sopenharmony_ci			cyy_init_card(cy_isa_address, 0);
32678c2ecf20Sopenharmony_ci		if (cy_isa_nchan == 0) {
32688c2ecf20Sopenharmony_ci			iounmap(cy_isa_address);
32698c2ecf20Sopenharmony_ci			continue;
32708c2ecf20Sopenharmony_ci		}
32718c2ecf20Sopenharmony_ci
32728c2ecf20Sopenharmony_ci		if (isparam && i < NR_CARDS && irq[i])
32738c2ecf20Sopenharmony_ci			cy_isa_irq = irq[i];
32748c2ecf20Sopenharmony_ci		else
32758c2ecf20Sopenharmony_ci			/* find out the board's irq by probing */
32768c2ecf20Sopenharmony_ci			cy_isa_irq = detect_isa_irq(cy_isa_address);
32778c2ecf20Sopenharmony_ci		if (cy_isa_irq == 0) {
32788c2ecf20Sopenharmony_ci			printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but the "
32798c2ecf20Sopenharmony_ci				"IRQ could not be detected.\n",
32808c2ecf20Sopenharmony_ci				(unsigned long)cy_isa_address);
32818c2ecf20Sopenharmony_ci			iounmap(cy_isa_address);
32828c2ecf20Sopenharmony_ci			continue;
32838c2ecf20Sopenharmony_ci		}
32848c2ecf20Sopenharmony_ci
32858c2ecf20Sopenharmony_ci		if ((cy_next_channel + cy_isa_nchan) > NR_PORTS) {
32868c2ecf20Sopenharmony_ci			printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but no "
32878c2ecf20Sopenharmony_ci				"more channels are available. Change NR_PORTS "
32888c2ecf20Sopenharmony_ci				"in cyclades.c and recompile kernel.\n",
32898c2ecf20Sopenharmony_ci				(unsigned long)cy_isa_address);
32908c2ecf20Sopenharmony_ci			iounmap(cy_isa_address);
32918c2ecf20Sopenharmony_ci			return nboard;
32928c2ecf20Sopenharmony_ci		}
32938c2ecf20Sopenharmony_ci		/* fill the next cy_card structure available */
32948c2ecf20Sopenharmony_ci		for (j = 0; j < NR_CARDS; j++) {
32958c2ecf20Sopenharmony_ci			card = &cy_card[j];
32968c2ecf20Sopenharmony_ci			if (card->base_addr == NULL)
32978c2ecf20Sopenharmony_ci				break;
32988c2ecf20Sopenharmony_ci		}
32998c2ecf20Sopenharmony_ci		if (j == NR_CARDS) {	/* no more cy_cards available */
33008c2ecf20Sopenharmony_ci			printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but no "
33018c2ecf20Sopenharmony_ci				"more cards can be used. Change NR_CARDS in "
33028c2ecf20Sopenharmony_ci				"cyclades.c and recompile kernel.\n",
33038c2ecf20Sopenharmony_ci				(unsigned long)cy_isa_address);
33048c2ecf20Sopenharmony_ci			iounmap(cy_isa_address);
33058c2ecf20Sopenharmony_ci			return nboard;
33068c2ecf20Sopenharmony_ci		}
33078c2ecf20Sopenharmony_ci
33088c2ecf20Sopenharmony_ci		/* allocate IRQ */
33098c2ecf20Sopenharmony_ci		if (request_irq(cy_isa_irq, cyy_interrupt,
33108c2ecf20Sopenharmony_ci				0, "Cyclom-Y", card)) {
33118c2ecf20Sopenharmony_ci			printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but "
33128c2ecf20Sopenharmony_ci				"could not allocate IRQ#%d.\n",
33138c2ecf20Sopenharmony_ci				(unsigned long)cy_isa_address, cy_isa_irq);
33148c2ecf20Sopenharmony_ci			iounmap(cy_isa_address);
33158c2ecf20Sopenharmony_ci			return nboard;
33168c2ecf20Sopenharmony_ci		}
33178c2ecf20Sopenharmony_ci
33188c2ecf20Sopenharmony_ci		/* set cy_card */
33198c2ecf20Sopenharmony_ci		card->base_addr = cy_isa_address;
33208c2ecf20Sopenharmony_ci		card->ctl_addr.p9050 = NULL;
33218c2ecf20Sopenharmony_ci		card->irq = (int)cy_isa_irq;
33228c2ecf20Sopenharmony_ci		card->bus_index = 0;
33238c2ecf20Sopenharmony_ci		card->first_line = cy_next_channel;
33248c2ecf20Sopenharmony_ci		card->num_chips = cy_isa_nchan / CyPORTS_PER_CHIP;
33258c2ecf20Sopenharmony_ci		card->nports = cy_isa_nchan;
33268c2ecf20Sopenharmony_ci		if (cy_init_card(card)) {
33278c2ecf20Sopenharmony_ci			card->base_addr = NULL;
33288c2ecf20Sopenharmony_ci			free_irq(cy_isa_irq, card);
33298c2ecf20Sopenharmony_ci			iounmap(cy_isa_address);
33308c2ecf20Sopenharmony_ci			continue;
33318c2ecf20Sopenharmony_ci		}
33328c2ecf20Sopenharmony_ci		nboard++;
33338c2ecf20Sopenharmony_ci
33348c2ecf20Sopenharmony_ci		printk(KERN_INFO "Cyclom-Y/ISA #%d: 0x%lx-0x%lx, IRQ%d found: "
33358c2ecf20Sopenharmony_ci			"%d channels starting from port %d\n",
33368c2ecf20Sopenharmony_ci			j + 1, (unsigned long)cy_isa_address,
33378c2ecf20Sopenharmony_ci			(unsigned long)(cy_isa_address + (CyISA_Ywin - 1)),
33388c2ecf20Sopenharmony_ci			cy_isa_irq, cy_isa_nchan, cy_next_channel);
33398c2ecf20Sopenharmony_ci
33408c2ecf20Sopenharmony_ci		for (k = 0, j = cy_next_channel;
33418c2ecf20Sopenharmony_ci				j < cy_next_channel + cy_isa_nchan; j++, k++)
33428c2ecf20Sopenharmony_ci			tty_port_register_device(&card->ports[k].port,
33438c2ecf20Sopenharmony_ci					cy_serial_driver, j, NULL);
33448c2ecf20Sopenharmony_ci		cy_next_channel += cy_isa_nchan;
33458c2ecf20Sopenharmony_ci	}
33468c2ecf20Sopenharmony_ci	return nboard;
33478c2ecf20Sopenharmony_ci#else
33488c2ecf20Sopenharmony_ci	return 0;
33498c2ecf20Sopenharmony_ci#endif				/* CONFIG_ISA */
33508c2ecf20Sopenharmony_ci}				/* cy_detect_isa */
33518c2ecf20Sopenharmony_ci
33528c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI
33538c2ecf20Sopenharmony_cistatic inline int cyc_isfwstr(const char *str, unsigned int size)
33548c2ecf20Sopenharmony_ci{
33558c2ecf20Sopenharmony_ci	unsigned int a;
33568c2ecf20Sopenharmony_ci
33578c2ecf20Sopenharmony_ci	for (a = 0; a < size && *str; a++, str++)
33588c2ecf20Sopenharmony_ci		if (*str & 0x80)
33598c2ecf20Sopenharmony_ci			return -EINVAL;
33608c2ecf20Sopenharmony_ci
33618c2ecf20Sopenharmony_ci	for (; a < size; a++, str++)
33628c2ecf20Sopenharmony_ci		if (*str)
33638c2ecf20Sopenharmony_ci			return -EINVAL;
33648c2ecf20Sopenharmony_ci
33658c2ecf20Sopenharmony_ci	return 0;
33668c2ecf20Sopenharmony_ci}
33678c2ecf20Sopenharmony_ci
33688c2ecf20Sopenharmony_cistatic inline void cyz_fpga_copy(void __iomem *fpga, const u8 *data,
33698c2ecf20Sopenharmony_ci		unsigned int size)
33708c2ecf20Sopenharmony_ci{
33718c2ecf20Sopenharmony_ci	for (; size > 0; size--) {
33728c2ecf20Sopenharmony_ci		cy_writel(fpga, *data++);
33738c2ecf20Sopenharmony_ci		udelay(10);
33748c2ecf20Sopenharmony_ci	}
33758c2ecf20Sopenharmony_ci}
33768c2ecf20Sopenharmony_ci
33778c2ecf20Sopenharmony_cistatic void plx_init(struct pci_dev *pdev, int irq,
33788c2ecf20Sopenharmony_ci		struct RUNTIME_9060 __iomem *addr)
33798c2ecf20Sopenharmony_ci{
33808c2ecf20Sopenharmony_ci	/* Reset PLX */
33818c2ecf20Sopenharmony_ci	cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) | 0x40000000);
33828c2ecf20Sopenharmony_ci	udelay(100L);
33838c2ecf20Sopenharmony_ci	cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) & ~0x40000000);
33848c2ecf20Sopenharmony_ci
33858c2ecf20Sopenharmony_ci	/* Reload Config. Registers from EEPROM */
33868c2ecf20Sopenharmony_ci	cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) | 0x20000000);
33878c2ecf20Sopenharmony_ci	udelay(100L);
33888c2ecf20Sopenharmony_ci	cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) & ~0x20000000);
33898c2ecf20Sopenharmony_ci
33908c2ecf20Sopenharmony_ci	/* For some yet unknown reason, once the PLX9060 reloads the EEPROM,
33918c2ecf20Sopenharmony_ci	 * the IRQ is lost and, thus, we have to re-write it to the PCI config.
33928c2ecf20Sopenharmony_ci	 * registers. This will remain here until we find a permanent fix.
33938c2ecf20Sopenharmony_ci	 */
33948c2ecf20Sopenharmony_ci	pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, irq);
33958c2ecf20Sopenharmony_ci}
33968c2ecf20Sopenharmony_ci
33978c2ecf20Sopenharmony_cistatic int __cyz_load_fw(const struct firmware *fw,
33988c2ecf20Sopenharmony_ci		const char *name, const u32 mailbox, void __iomem *base,
33998c2ecf20Sopenharmony_ci		void __iomem *fpga)
34008c2ecf20Sopenharmony_ci{
34018c2ecf20Sopenharmony_ci	const void *ptr = fw->data;
34028c2ecf20Sopenharmony_ci	const struct zfile_header *h = ptr;
34038c2ecf20Sopenharmony_ci	const struct zfile_config *c, *cs;
34048c2ecf20Sopenharmony_ci	const struct zfile_block *b, *bs;
34058c2ecf20Sopenharmony_ci	unsigned int a, tmp, len = fw->size;
34068c2ecf20Sopenharmony_ci#define BAD_FW KERN_ERR "Bad firmware: "
34078c2ecf20Sopenharmony_ci	if (len < sizeof(*h)) {
34088c2ecf20Sopenharmony_ci		printk(BAD_FW "too short: %u<%zu\n", len, sizeof(*h));
34098c2ecf20Sopenharmony_ci		return -EINVAL;
34108c2ecf20Sopenharmony_ci	}
34118c2ecf20Sopenharmony_ci
34128c2ecf20Sopenharmony_ci	cs = ptr + h->config_offset;
34138c2ecf20Sopenharmony_ci	bs = ptr + h->block_offset;
34148c2ecf20Sopenharmony_ci
34158c2ecf20Sopenharmony_ci	if ((void *)(cs + h->n_config) > ptr + len ||
34168c2ecf20Sopenharmony_ci			(void *)(bs + h->n_blocks) > ptr + len) {
34178c2ecf20Sopenharmony_ci		printk(BAD_FW "too short");
34188c2ecf20Sopenharmony_ci		return  -EINVAL;
34198c2ecf20Sopenharmony_ci	}
34208c2ecf20Sopenharmony_ci
34218c2ecf20Sopenharmony_ci	if (cyc_isfwstr(h->name, sizeof(h->name)) ||
34228c2ecf20Sopenharmony_ci			cyc_isfwstr(h->date, sizeof(h->date))) {
34238c2ecf20Sopenharmony_ci		printk(BAD_FW "bad formatted header string\n");
34248c2ecf20Sopenharmony_ci		return -EINVAL;
34258c2ecf20Sopenharmony_ci	}
34268c2ecf20Sopenharmony_ci
34278c2ecf20Sopenharmony_ci	if (strncmp(name, h->name, sizeof(h->name))) {
34288c2ecf20Sopenharmony_ci		printk(BAD_FW "bad name '%s' (expected '%s')\n", h->name, name);
34298c2ecf20Sopenharmony_ci		return -EINVAL;
34308c2ecf20Sopenharmony_ci	}
34318c2ecf20Sopenharmony_ci
34328c2ecf20Sopenharmony_ci	tmp = 0;
34338c2ecf20Sopenharmony_ci	for (c = cs; c < cs + h->n_config; c++) {
34348c2ecf20Sopenharmony_ci		for (a = 0; a < c->n_blocks; a++)
34358c2ecf20Sopenharmony_ci			if (c->block_list[a] > h->n_blocks) {
34368c2ecf20Sopenharmony_ci				printk(BAD_FW "bad block ref number in cfgs\n");
34378c2ecf20Sopenharmony_ci				return -EINVAL;
34388c2ecf20Sopenharmony_ci			}
34398c2ecf20Sopenharmony_ci		if (c->mailbox == mailbox && c->function == 0) /* 0 is normal */
34408c2ecf20Sopenharmony_ci			tmp++;
34418c2ecf20Sopenharmony_ci	}
34428c2ecf20Sopenharmony_ci	if (!tmp) {
34438c2ecf20Sopenharmony_ci		printk(BAD_FW "nothing appropriate\n");
34448c2ecf20Sopenharmony_ci		return -EINVAL;
34458c2ecf20Sopenharmony_ci	}
34468c2ecf20Sopenharmony_ci
34478c2ecf20Sopenharmony_ci	for (b = bs; b < bs + h->n_blocks; b++)
34488c2ecf20Sopenharmony_ci		if (b->file_offset + b->size > len) {
34498c2ecf20Sopenharmony_ci			printk(BAD_FW "bad block data offset\n");
34508c2ecf20Sopenharmony_ci			return -EINVAL;
34518c2ecf20Sopenharmony_ci		}
34528c2ecf20Sopenharmony_ci
34538c2ecf20Sopenharmony_ci	/* everything is OK, let's seek'n'load it */
34548c2ecf20Sopenharmony_ci	for (c = cs; c < cs + h->n_config; c++)
34558c2ecf20Sopenharmony_ci		if (c->mailbox == mailbox && c->function == 0)
34568c2ecf20Sopenharmony_ci			break;
34578c2ecf20Sopenharmony_ci
34588c2ecf20Sopenharmony_ci	for (a = 0; a < c->n_blocks; a++) {
34598c2ecf20Sopenharmony_ci		b = &bs[c->block_list[a]];
34608c2ecf20Sopenharmony_ci		if (b->type == ZBLOCK_FPGA) {
34618c2ecf20Sopenharmony_ci			if (fpga != NULL)
34628c2ecf20Sopenharmony_ci				cyz_fpga_copy(fpga, ptr + b->file_offset,
34638c2ecf20Sopenharmony_ci						b->size);
34648c2ecf20Sopenharmony_ci		} else {
34658c2ecf20Sopenharmony_ci			if (base != NULL)
34668c2ecf20Sopenharmony_ci				memcpy_toio(base + b->ram_offset,
34678c2ecf20Sopenharmony_ci					       ptr + b->file_offset, b->size);
34688c2ecf20Sopenharmony_ci		}
34698c2ecf20Sopenharmony_ci	}
34708c2ecf20Sopenharmony_ci#undef BAD_FW
34718c2ecf20Sopenharmony_ci	return 0;
34728c2ecf20Sopenharmony_ci}
34738c2ecf20Sopenharmony_ci
34748c2ecf20Sopenharmony_cistatic int cyz_load_fw(struct pci_dev *pdev, void __iomem *base_addr,
34758c2ecf20Sopenharmony_ci		struct RUNTIME_9060 __iomem *ctl_addr, int irq)
34768c2ecf20Sopenharmony_ci{
34778c2ecf20Sopenharmony_ci	const struct firmware *fw;
34788c2ecf20Sopenharmony_ci	struct FIRM_ID __iomem *fid = base_addr + ID_ADDRESS;
34798c2ecf20Sopenharmony_ci	struct CUSTOM_REG __iomem *cust = base_addr;
34808c2ecf20Sopenharmony_ci	struct ZFW_CTRL __iomem *pt_zfwctrl;
34818c2ecf20Sopenharmony_ci	void __iomem *tmp;
34828c2ecf20Sopenharmony_ci	u32 mailbox, status, nchan;
34838c2ecf20Sopenharmony_ci	unsigned int i;
34848c2ecf20Sopenharmony_ci	int retval;
34858c2ecf20Sopenharmony_ci
34868c2ecf20Sopenharmony_ci	retval = request_firmware(&fw, "cyzfirm.bin", &pdev->dev);
34878c2ecf20Sopenharmony_ci	if (retval) {
34888c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "can't get firmware\n");
34898c2ecf20Sopenharmony_ci		goto err;
34908c2ecf20Sopenharmony_ci	}
34918c2ecf20Sopenharmony_ci
34928c2ecf20Sopenharmony_ci	/* Check whether the firmware is already loaded and running. If
34938c2ecf20Sopenharmony_ci	   positive, skip this board */
34948c2ecf20Sopenharmony_ci	if (__cyz_fpga_loaded(ctl_addr) && readl(&fid->signature) == ZFIRM_ID) {
34958c2ecf20Sopenharmony_ci		u32 cntval = readl(base_addr + 0x190);
34968c2ecf20Sopenharmony_ci
34978c2ecf20Sopenharmony_ci		udelay(100);
34988c2ecf20Sopenharmony_ci		if (cntval != readl(base_addr + 0x190)) {
34998c2ecf20Sopenharmony_ci			/* FW counter is working, FW is running */
35008c2ecf20Sopenharmony_ci			dev_dbg(&pdev->dev, "Cyclades-Z FW already loaded. "
35018c2ecf20Sopenharmony_ci					"Skipping board.\n");
35028c2ecf20Sopenharmony_ci			retval = 0;
35038c2ecf20Sopenharmony_ci			goto err_rel;
35048c2ecf20Sopenharmony_ci		}
35058c2ecf20Sopenharmony_ci	}
35068c2ecf20Sopenharmony_ci
35078c2ecf20Sopenharmony_ci	/* start boot */
35088c2ecf20Sopenharmony_ci	cy_writel(&ctl_addr->intr_ctrl_stat, readl(&ctl_addr->intr_ctrl_stat) &
35098c2ecf20Sopenharmony_ci			~0x00030800UL);
35108c2ecf20Sopenharmony_ci
35118c2ecf20Sopenharmony_ci	mailbox = readl(&ctl_addr->mail_box_0);
35128c2ecf20Sopenharmony_ci
35138c2ecf20Sopenharmony_ci	if (mailbox == 0 || __cyz_fpga_loaded(ctl_addr)) {
35148c2ecf20Sopenharmony_ci		/* stops CPU and set window to beginning of RAM */
35158c2ecf20Sopenharmony_ci		cy_writel(&ctl_addr->loc_addr_base, WIN_CREG);
35168c2ecf20Sopenharmony_ci		cy_writel(&cust->cpu_stop, 0);
35178c2ecf20Sopenharmony_ci		cy_writel(&ctl_addr->loc_addr_base, WIN_RAM);
35188c2ecf20Sopenharmony_ci		udelay(100);
35198c2ecf20Sopenharmony_ci	}
35208c2ecf20Sopenharmony_ci
35218c2ecf20Sopenharmony_ci	plx_init(pdev, irq, ctl_addr);
35228c2ecf20Sopenharmony_ci
35238c2ecf20Sopenharmony_ci	if (mailbox != 0) {
35248c2ecf20Sopenharmony_ci		/* load FPGA */
35258c2ecf20Sopenharmony_ci		retval = __cyz_load_fw(fw, "Cyclom-Z", mailbox, NULL,
35268c2ecf20Sopenharmony_ci				base_addr);
35278c2ecf20Sopenharmony_ci		if (retval)
35288c2ecf20Sopenharmony_ci			goto err_rel;
35298c2ecf20Sopenharmony_ci		if (!__cyz_fpga_loaded(ctl_addr)) {
35308c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "fw upload successful, but fw is "
35318c2ecf20Sopenharmony_ci					"not loaded\n");
35328c2ecf20Sopenharmony_ci			goto err_rel;
35338c2ecf20Sopenharmony_ci		}
35348c2ecf20Sopenharmony_ci	}
35358c2ecf20Sopenharmony_ci
35368c2ecf20Sopenharmony_ci	/* stops CPU and set window to beginning of RAM */
35378c2ecf20Sopenharmony_ci	cy_writel(&ctl_addr->loc_addr_base, WIN_CREG);
35388c2ecf20Sopenharmony_ci	cy_writel(&cust->cpu_stop, 0);
35398c2ecf20Sopenharmony_ci	cy_writel(&ctl_addr->loc_addr_base, WIN_RAM);
35408c2ecf20Sopenharmony_ci	udelay(100);
35418c2ecf20Sopenharmony_ci
35428c2ecf20Sopenharmony_ci	/* clear memory */
35438c2ecf20Sopenharmony_ci	for (tmp = base_addr; tmp < base_addr + RAM_SIZE; tmp++)
35448c2ecf20Sopenharmony_ci		cy_writeb(tmp, 255);
35458c2ecf20Sopenharmony_ci	if (mailbox != 0) {
35468c2ecf20Sopenharmony_ci		/* set window to last 512K of RAM */
35478c2ecf20Sopenharmony_ci		cy_writel(&ctl_addr->loc_addr_base, WIN_RAM + RAM_SIZE);
35488c2ecf20Sopenharmony_ci		for (tmp = base_addr; tmp < base_addr + RAM_SIZE; tmp++)
35498c2ecf20Sopenharmony_ci			cy_writeb(tmp, 255);
35508c2ecf20Sopenharmony_ci		/* set window to beginning of RAM */
35518c2ecf20Sopenharmony_ci		cy_writel(&ctl_addr->loc_addr_base, WIN_RAM);
35528c2ecf20Sopenharmony_ci	}
35538c2ecf20Sopenharmony_ci
35548c2ecf20Sopenharmony_ci	retval = __cyz_load_fw(fw, "Cyclom-Z", mailbox, base_addr, NULL);
35558c2ecf20Sopenharmony_ci	release_firmware(fw);
35568c2ecf20Sopenharmony_ci	if (retval)
35578c2ecf20Sopenharmony_ci		goto err;
35588c2ecf20Sopenharmony_ci
35598c2ecf20Sopenharmony_ci	/* finish boot and start boards */
35608c2ecf20Sopenharmony_ci	cy_writel(&ctl_addr->loc_addr_base, WIN_CREG);
35618c2ecf20Sopenharmony_ci	cy_writel(&cust->cpu_start, 0);
35628c2ecf20Sopenharmony_ci	cy_writel(&ctl_addr->loc_addr_base, WIN_RAM);
35638c2ecf20Sopenharmony_ci	i = 0;
35648c2ecf20Sopenharmony_ci	while ((status = readl(&fid->signature)) != ZFIRM_ID && i++ < 40)
35658c2ecf20Sopenharmony_ci		msleep(100);
35668c2ecf20Sopenharmony_ci	if (status != ZFIRM_ID) {
35678c2ecf20Sopenharmony_ci		if (status == ZFIRM_HLT) {
35688c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "you need an external power supply "
35698c2ecf20Sopenharmony_ci				"for this number of ports. Firmware halted and "
35708c2ecf20Sopenharmony_ci				"board reset.\n");
35718c2ecf20Sopenharmony_ci			retval = -EIO;
35728c2ecf20Sopenharmony_ci			goto err;
35738c2ecf20Sopenharmony_ci		}
35748c2ecf20Sopenharmony_ci		dev_warn(&pdev->dev, "fid->signature = 0x%x... Waiting "
35758c2ecf20Sopenharmony_ci				"some more time\n", status);
35768c2ecf20Sopenharmony_ci		while ((status = readl(&fid->signature)) != ZFIRM_ID &&
35778c2ecf20Sopenharmony_ci				i++ < 200)
35788c2ecf20Sopenharmony_ci			msleep(100);
35798c2ecf20Sopenharmony_ci		if (status != ZFIRM_ID) {
35808c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "Board not started in 20 seconds! "
35818c2ecf20Sopenharmony_ci					"Giving up. (fid->signature = 0x%x)\n",
35828c2ecf20Sopenharmony_ci					status);
35838c2ecf20Sopenharmony_ci			dev_info(&pdev->dev, "*** Warning ***: if you are "
35848c2ecf20Sopenharmony_ci				"upgrading the FW, please power cycle the "
35858c2ecf20Sopenharmony_ci				"system before loading the new FW to the "
35868c2ecf20Sopenharmony_ci				"Cyclades-Z.\n");
35878c2ecf20Sopenharmony_ci
35888c2ecf20Sopenharmony_ci			if (__cyz_fpga_loaded(ctl_addr))
35898c2ecf20Sopenharmony_ci				plx_init(pdev, irq, ctl_addr);
35908c2ecf20Sopenharmony_ci
35918c2ecf20Sopenharmony_ci			retval = -EIO;
35928c2ecf20Sopenharmony_ci			goto err;
35938c2ecf20Sopenharmony_ci		}
35948c2ecf20Sopenharmony_ci		dev_dbg(&pdev->dev, "Firmware started after %d seconds.\n",
35958c2ecf20Sopenharmony_ci				i / 10);
35968c2ecf20Sopenharmony_ci	}
35978c2ecf20Sopenharmony_ci	pt_zfwctrl = base_addr + readl(&fid->zfwctrl_addr);
35988c2ecf20Sopenharmony_ci
35998c2ecf20Sopenharmony_ci	dev_dbg(&pdev->dev, "fid=> %p, zfwctrl_addr=> %x, npt_zfwctrl=> %p\n",
36008c2ecf20Sopenharmony_ci			base_addr + ID_ADDRESS, readl(&fid->zfwctrl_addr),
36018c2ecf20Sopenharmony_ci			base_addr + readl(&fid->zfwctrl_addr));
36028c2ecf20Sopenharmony_ci
36038c2ecf20Sopenharmony_ci	nchan = readl(&pt_zfwctrl->board_ctrl.n_channel);
36048c2ecf20Sopenharmony_ci	dev_info(&pdev->dev, "Cyclades-Z FW loaded: version = %x, ports = %u\n",
36058c2ecf20Sopenharmony_ci		readl(&pt_zfwctrl->board_ctrl.fw_version), nchan);
36068c2ecf20Sopenharmony_ci
36078c2ecf20Sopenharmony_ci	if (nchan == 0) {
36088c2ecf20Sopenharmony_ci		dev_warn(&pdev->dev, "no Cyclades-Z ports were found. Please "
36098c2ecf20Sopenharmony_ci			"check the connection between the Z host card and the "
36108c2ecf20Sopenharmony_ci			"serial expanders.\n");
36118c2ecf20Sopenharmony_ci
36128c2ecf20Sopenharmony_ci		if (__cyz_fpga_loaded(ctl_addr))
36138c2ecf20Sopenharmony_ci			plx_init(pdev, irq, ctl_addr);
36148c2ecf20Sopenharmony_ci
36158c2ecf20Sopenharmony_ci		dev_info(&pdev->dev, "Null number of ports detected. Board "
36168c2ecf20Sopenharmony_ci				"reset.\n");
36178c2ecf20Sopenharmony_ci		retval = 0;
36188c2ecf20Sopenharmony_ci		goto err;
36198c2ecf20Sopenharmony_ci	}
36208c2ecf20Sopenharmony_ci
36218c2ecf20Sopenharmony_ci	cy_writel(&pt_zfwctrl->board_ctrl.op_system, C_OS_LINUX);
36228c2ecf20Sopenharmony_ci	cy_writel(&pt_zfwctrl->board_ctrl.dr_version, DRIVER_VERSION);
36238c2ecf20Sopenharmony_ci
36248c2ecf20Sopenharmony_ci	/*
36258c2ecf20Sopenharmony_ci	   Early firmware failed to start looking for commands.
36268c2ecf20Sopenharmony_ci	   This enables firmware interrupts for those commands.
36278c2ecf20Sopenharmony_ci	 */
36288c2ecf20Sopenharmony_ci	cy_writel(&ctl_addr->intr_ctrl_stat, readl(&ctl_addr->intr_ctrl_stat) |
36298c2ecf20Sopenharmony_ci			(1 << 17));
36308c2ecf20Sopenharmony_ci	cy_writel(&ctl_addr->intr_ctrl_stat, readl(&ctl_addr->intr_ctrl_stat) |
36318c2ecf20Sopenharmony_ci			0x00030800UL);
36328c2ecf20Sopenharmony_ci
36338c2ecf20Sopenharmony_ci	return nchan;
36348c2ecf20Sopenharmony_cierr_rel:
36358c2ecf20Sopenharmony_ci	release_firmware(fw);
36368c2ecf20Sopenharmony_cierr:
36378c2ecf20Sopenharmony_ci	return retval;
36388c2ecf20Sopenharmony_ci}
36398c2ecf20Sopenharmony_ci
36408c2ecf20Sopenharmony_cistatic int cy_pci_probe(struct pci_dev *pdev,
36418c2ecf20Sopenharmony_ci		const struct pci_device_id *ent)
36428c2ecf20Sopenharmony_ci{
36438c2ecf20Sopenharmony_ci	struct cyclades_card *card;
36448c2ecf20Sopenharmony_ci	void __iomem *addr0 = NULL, *addr2 = NULL;
36458c2ecf20Sopenharmony_ci	char *card_name = NULL;
36468c2ecf20Sopenharmony_ci	u32 mailbox;
36478c2ecf20Sopenharmony_ci	unsigned int device_id, nchan = 0, card_no, i, j;
36488c2ecf20Sopenharmony_ci	unsigned char plx_ver;
36498c2ecf20Sopenharmony_ci	int retval, irq;
36508c2ecf20Sopenharmony_ci
36518c2ecf20Sopenharmony_ci	retval = pci_enable_device(pdev);
36528c2ecf20Sopenharmony_ci	if (retval) {
36538c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "cannot enable device\n");
36548c2ecf20Sopenharmony_ci		goto err;
36558c2ecf20Sopenharmony_ci	}
36568c2ecf20Sopenharmony_ci
36578c2ecf20Sopenharmony_ci	/* read PCI configuration area */
36588c2ecf20Sopenharmony_ci	irq = pdev->irq;
36598c2ecf20Sopenharmony_ci	device_id = pdev->device & ~PCI_DEVICE_ID_MASK;
36608c2ecf20Sopenharmony_ci
36618c2ecf20Sopenharmony_ci#if defined(__alpha__)
36628c2ecf20Sopenharmony_ci	if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo) {	/* below 1M? */
36638c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Cyclom-Y/PCI not supported for low "
36648c2ecf20Sopenharmony_ci			"addresses on Alpha systems.\n");
36658c2ecf20Sopenharmony_ci		retval = -EIO;
36668c2ecf20Sopenharmony_ci		goto err_dis;
36678c2ecf20Sopenharmony_ci	}
36688c2ecf20Sopenharmony_ci#endif
36698c2ecf20Sopenharmony_ci	if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Lo) {
36708c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Cyclades-Z/PCI not supported for low "
36718c2ecf20Sopenharmony_ci			"addresses\n");
36728c2ecf20Sopenharmony_ci		retval = -EIO;
36738c2ecf20Sopenharmony_ci		goto err_dis;
36748c2ecf20Sopenharmony_ci	}
36758c2ecf20Sopenharmony_ci
36768c2ecf20Sopenharmony_ci	if (pci_resource_flags(pdev, 2) & IORESOURCE_IO) {
36778c2ecf20Sopenharmony_ci		dev_warn(&pdev->dev, "PCI I/O bit incorrectly set. Ignoring "
36788c2ecf20Sopenharmony_ci				"it...\n");
36798c2ecf20Sopenharmony_ci		pdev->resource[2].flags &= ~IORESOURCE_IO;
36808c2ecf20Sopenharmony_ci	}
36818c2ecf20Sopenharmony_ci
36828c2ecf20Sopenharmony_ci	retval = pci_request_regions(pdev, "cyclades");
36838c2ecf20Sopenharmony_ci	if (retval) {
36848c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to reserve resources\n");
36858c2ecf20Sopenharmony_ci		goto err_dis;
36868c2ecf20Sopenharmony_ci	}
36878c2ecf20Sopenharmony_ci
36888c2ecf20Sopenharmony_ci	retval = -EIO;
36898c2ecf20Sopenharmony_ci	if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo ||
36908c2ecf20Sopenharmony_ci			device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) {
36918c2ecf20Sopenharmony_ci		card_name = "Cyclom-Y";
36928c2ecf20Sopenharmony_ci
36938c2ecf20Sopenharmony_ci		addr0 = ioremap(pci_resource_start(pdev, 0),
36948c2ecf20Sopenharmony_ci				CyPCI_Yctl);
36958c2ecf20Sopenharmony_ci		if (addr0 == NULL) {
36968c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "can't remap ctl region\n");
36978c2ecf20Sopenharmony_ci			goto err_reg;
36988c2ecf20Sopenharmony_ci		}
36998c2ecf20Sopenharmony_ci		addr2 = ioremap(pci_resource_start(pdev, 2),
37008c2ecf20Sopenharmony_ci				CyPCI_Ywin);
37018c2ecf20Sopenharmony_ci		if (addr2 == NULL) {
37028c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "can't remap base region\n");
37038c2ecf20Sopenharmony_ci			goto err_unmap;
37048c2ecf20Sopenharmony_ci		}
37058c2ecf20Sopenharmony_ci
37068c2ecf20Sopenharmony_ci		nchan = CyPORTS_PER_CHIP * cyy_init_card(addr2, 1);
37078c2ecf20Sopenharmony_ci		if (nchan == 0) {
37088c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "Cyclom-Y PCI host card with no "
37098c2ecf20Sopenharmony_ci					"Serial-Modules\n");
37108c2ecf20Sopenharmony_ci			goto err_unmap;
37118c2ecf20Sopenharmony_ci		}
37128c2ecf20Sopenharmony_ci	} else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Hi) {
37138c2ecf20Sopenharmony_ci		struct RUNTIME_9060 __iomem *ctl_addr;
37148c2ecf20Sopenharmony_ci
37158c2ecf20Sopenharmony_ci		ctl_addr = addr0 = ioremap(pci_resource_start(pdev, 0),
37168c2ecf20Sopenharmony_ci				CyPCI_Zctl);
37178c2ecf20Sopenharmony_ci		if (addr0 == NULL) {
37188c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "can't remap ctl region\n");
37198c2ecf20Sopenharmony_ci			goto err_reg;
37208c2ecf20Sopenharmony_ci		}
37218c2ecf20Sopenharmony_ci
37228c2ecf20Sopenharmony_ci		/* Disable interrupts on the PLX before resetting it */
37238c2ecf20Sopenharmony_ci		cy_writew(&ctl_addr->intr_ctrl_stat,
37248c2ecf20Sopenharmony_ci				readw(&ctl_addr->intr_ctrl_stat) & ~0x0900);
37258c2ecf20Sopenharmony_ci
37268c2ecf20Sopenharmony_ci		plx_init(pdev, irq, addr0);
37278c2ecf20Sopenharmony_ci
37288c2ecf20Sopenharmony_ci		mailbox = readl(&ctl_addr->mail_box_0);
37298c2ecf20Sopenharmony_ci
37308c2ecf20Sopenharmony_ci		addr2 = ioremap(pci_resource_start(pdev, 2),
37318c2ecf20Sopenharmony_ci				mailbox == ZE_V1 ? CyPCI_Ze_win : CyPCI_Zwin);
37328c2ecf20Sopenharmony_ci		if (addr2 == NULL) {
37338c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "can't remap base region\n");
37348c2ecf20Sopenharmony_ci			goto err_unmap;
37358c2ecf20Sopenharmony_ci		}
37368c2ecf20Sopenharmony_ci
37378c2ecf20Sopenharmony_ci		if (mailbox == ZE_V1) {
37388c2ecf20Sopenharmony_ci			card_name = "Cyclades-Ze";
37398c2ecf20Sopenharmony_ci		} else {
37408c2ecf20Sopenharmony_ci			card_name = "Cyclades-8Zo";
37418c2ecf20Sopenharmony_ci#ifdef CY_PCI_DEBUG
37428c2ecf20Sopenharmony_ci			if (mailbox == ZO_V1) {
37438c2ecf20Sopenharmony_ci				cy_writel(&ctl_addr->loc_addr_base, WIN_CREG);
37448c2ecf20Sopenharmony_ci				dev_info(&pdev->dev, "Cyclades-8Zo/PCI: FPGA "
37458c2ecf20Sopenharmony_ci					"id %lx, ver %lx\n", (ulong)(0xff &
37468c2ecf20Sopenharmony_ci					readl(&((struct CUSTOM_REG *)addr2)->
37478c2ecf20Sopenharmony_ci						fpga_id)), (ulong)(0xff &
37488c2ecf20Sopenharmony_ci					readl(&((struct CUSTOM_REG *)addr2)->
37498c2ecf20Sopenharmony_ci						fpga_version)));
37508c2ecf20Sopenharmony_ci				cy_writel(&ctl_addr->loc_addr_base, WIN_RAM);
37518c2ecf20Sopenharmony_ci			} else {
37528c2ecf20Sopenharmony_ci				dev_info(&pdev->dev, "Cyclades-Z/PCI: New "
37538c2ecf20Sopenharmony_ci					"Cyclades-Z board.  FPGA not loaded\n");
37548c2ecf20Sopenharmony_ci			}
37558c2ecf20Sopenharmony_ci#endif
37568c2ecf20Sopenharmony_ci			/* The following clears the firmware id word.  This
37578c2ecf20Sopenharmony_ci			   ensures that the driver will not attempt to talk to
37588c2ecf20Sopenharmony_ci			   the board until it has been properly initialized.
37598c2ecf20Sopenharmony_ci			 */
37608c2ecf20Sopenharmony_ci			if ((mailbox == ZO_V1) || (mailbox == ZO_V2))
37618c2ecf20Sopenharmony_ci				cy_writel(addr2 + ID_ADDRESS, 0L);
37628c2ecf20Sopenharmony_ci		}
37638c2ecf20Sopenharmony_ci
37648c2ecf20Sopenharmony_ci		retval = cyz_load_fw(pdev, addr2, addr0, irq);
37658c2ecf20Sopenharmony_ci		if (retval <= 0)
37668c2ecf20Sopenharmony_ci			goto err_unmap;
37678c2ecf20Sopenharmony_ci		nchan = retval;
37688c2ecf20Sopenharmony_ci	}
37698c2ecf20Sopenharmony_ci
37708c2ecf20Sopenharmony_ci	if ((cy_next_channel + nchan) > NR_PORTS) {
37718c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Cyclades-8Zo/PCI found, but no "
37728c2ecf20Sopenharmony_ci			"channels are available. Change NR_PORTS in "
37738c2ecf20Sopenharmony_ci			"cyclades.c and recompile kernel.\n");
37748c2ecf20Sopenharmony_ci		goto err_unmap;
37758c2ecf20Sopenharmony_ci	}
37768c2ecf20Sopenharmony_ci	/* fill the next cy_card structure available */
37778c2ecf20Sopenharmony_ci	for (card_no = 0; card_no < NR_CARDS; card_no++) {
37788c2ecf20Sopenharmony_ci		card = &cy_card[card_no];
37798c2ecf20Sopenharmony_ci		if (card->base_addr == NULL)
37808c2ecf20Sopenharmony_ci			break;
37818c2ecf20Sopenharmony_ci	}
37828c2ecf20Sopenharmony_ci	if (card_no == NR_CARDS) {	/* no more cy_cards available */
37838c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Cyclades-8Zo/PCI found, but no "
37848c2ecf20Sopenharmony_ci			"more cards can be used. Change NR_CARDS in "
37858c2ecf20Sopenharmony_ci			"cyclades.c and recompile kernel.\n");
37868c2ecf20Sopenharmony_ci		goto err_unmap;
37878c2ecf20Sopenharmony_ci	}
37888c2ecf20Sopenharmony_ci
37898c2ecf20Sopenharmony_ci	if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo ||
37908c2ecf20Sopenharmony_ci			device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) {
37918c2ecf20Sopenharmony_ci		/* allocate IRQ */
37928c2ecf20Sopenharmony_ci		retval = request_irq(irq, cyy_interrupt,
37938c2ecf20Sopenharmony_ci				IRQF_SHARED, "Cyclom-Y", card);
37948c2ecf20Sopenharmony_ci		if (retval) {
37958c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "could not allocate IRQ\n");
37968c2ecf20Sopenharmony_ci			goto err_unmap;
37978c2ecf20Sopenharmony_ci		}
37988c2ecf20Sopenharmony_ci		card->num_chips = nchan / CyPORTS_PER_CHIP;
37998c2ecf20Sopenharmony_ci	} else {
38008c2ecf20Sopenharmony_ci		struct FIRM_ID __iomem *firm_id = addr2 + ID_ADDRESS;
38018c2ecf20Sopenharmony_ci		struct ZFW_CTRL __iomem *zfw_ctrl;
38028c2ecf20Sopenharmony_ci
38038c2ecf20Sopenharmony_ci		zfw_ctrl = addr2 + (readl(&firm_id->zfwctrl_addr) & 0xfffff);
38048c2ecf20Sopenharmony_ci
38058c2ecf20Sopenharmony_ci		card->hw_ver = mailbox;
38068c2ecf20Sopenharmony_ci		card->num_chips = (unsigned int)-1;
38078c2ecf20Sopenharmony_ci		card->board_ctrl = &zfw_ctrl->board_ctrl;
38088c2ecf20Sopenharmony_ci#ifdef CONFIG_CYZ_INTR
38098c2ecf20Sopenharmony_ci		/* allocate IRQ only if board has an IRQ */
38108c2ecf20Sopenharmony_ci		if (irq != 0 && irq != 255) {
38118c2ecf20Sopenharmony_ci			retval = request_irq(irq, cyz_interrupt,
38128c2ecf20Sopenharmony_ci					IRQF_SHARED, "Cyclades-Z", card);
38138c2ecf20Sopenharmony_ci			if (retval) {
38148c2ecf20Sopenharmony_ci				dev_err(&pdev->dev, "could not allocate IRQ\n");
38158c2ecf20Sopenharmony_ci				goto err_unmap;
38168c2ecf20Sopenharmony_ci			}
38178c2ecf20Sopenharmony_ci		}
38188c2ecf20Sopenharmony_ci#endif				/* CONFIG_CYZ_INTR */
38198c2ecf20Sopenharmony_ci	}
38208c2ecf20Sopenharmony_ci
38218c2ecf20Sopenharmony_ci	/* set cy_card */
38228c2ecf20Sopenharmony_ci	card->base_addr = addr2;
38238c2ecf20Sopenharmony_ci	card->ctl_addr.p9050 = addr0;
38248c2ecf20Sopenharmony_ci	card->irq = irq;
38258c2ecf20Sopenharmony_ci	card->bus_index = 1;
38268c2ecf20Sopenharmony_ci	card->first_line = cy_next_channel;
38278c2ecf20Sopenharmony_ci	card->nports = nchan;
38288c2ecf20Sopenharmony_ci	retval = cy_init_card(card);
38298c2ecf20Sopenharmony_ci	if (retval)
38308c2ecf20Sopenharmony_ci		goto err_null;
38318c2ecf20Sopenharmony_ci
38328c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, card);
38338c2ecf20Sopenharmony_ci
38348c2ecf20Sopenharmony_ci	if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo ||
38358c2ecf20Sopenharmony_ci			device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) {
38368c2ecf20Sopenharmony_ci		/* enable interrupts in the PCI interface */
38378c2ecf20Sopenharmony_ci		plx_ver = readb(addr2 + CyPLX_VER) & 0x0f;
38388c2ecf20Sopenharmony_ci		switch (plx_ver) {
38398c2ecf20Sopenharmony_ci		case PLX_9050:
38408c2ecf20Sopenharmony_ci			cy_writeb(addr0 + 0x4c, 0x43);
38418c2ecf20Sopenharmony_ci			break;
38428c2ecf20Sopenharmony_ci
38438c2ecf20Sopenharmony_ci		case PLX_9060:
38448c2ecf20Sopenharmony_ci		case PLX_9080:
38458c2ecf20Sopenharmony_ci		default:	/* Old boards, use PLX_9060 */
38468c2ecf20Sopenharmony_ci		{
38478c2ecf20Sopenharmony_ci			struct RUNTIME_9060 __iomem *ctl_addr = addr0;
38488c2ecf20Sopenharmony_ci			plx_init(pdev, irq, ctl_addr);
38498c2ecf20Sopenharmony_ci			cy_writew(&ctl_addr->intr_ctrl_stat,
38508c2ecf20Sopenharmony_ci				readw(&ctl_addr->intr_ctrl_stat) | 0x0900);
38518c2ecf20Sopenharmony_ci			break;
38528c2ecf20Sopenharmony_ci		}
38538c2ecf20Sopenharmony_ci		}
38548c2ecf20Sopenharmony_ci	}
38558c2ecf20Sopenharmony_ci
38568c2ecf20Sopenharmony_ci	dev_info(&pdev->dev, "%s/PCI #%d found: %d channels starting from "
38578c2ecf20Sopenharmony_ci		"port %d.\n", card_name, card_no + 1, nchan, cy_next_channel);
38588c2ecf20Sopenharmony_ci	for (j = 0, i = cy_next_channel; i < cy_next_channel + nchan; i++, j++)
38598c2ecf20Sopenharmony_ci		tty_port_register_device(&card->ports[j].port,
38608c2ecf20Sopenharmony_ci				cy_serial_driver, i, &pdev->dev);
38618c2ecf20Sopenharmony_ci	cy_next_channel += nchan;
38628c2ecf20Sopenharmony_ci
38638c2ecf20Sopenharmony_ci	return 0;
38648c2ecf20Sopenharmony_cierr_null:
38658c2ecf20Sopenharmony_ci	card->base_addr = NULL;
38668c2ecf20Sopenharmony_ci	free_irq(irq, card);
38678c2ecf20Sopenharmony_cierr_unmap:
38688c2ecf20Sopenharmony_ci	iounmap(addr0);
38698c2ecf20Sopenharmony_ci	if (addr2)
38708c2ecf20Sopenharmony_ci		iounmap(addr2);
38718c2ecf20Sopenharmony_cierr_reg:
38728c2ecf20Sopenharmony_ci	pci_release_regions(pdev);
38738c2ecf20Sopenharmony_cierr_dis:
38748c2ecf20Sopenharmony_ci	pci_disable_device(pdev);
38758c2ecf20Sopenharmony_cierr:
38768c2ecf20Sopenharmony_ci	return retval;
38778c2ecf20Sopenharmony_ci}
38788c2ecf20Sopenharmony_ci
38798c2ecf20Sopenharmony_cistatic void cy_pci_remove(struct pci_dev *pdev)
38808c2ecf20Sopenharmony_ci{
38818c2ecf20Sopenharmony_ci	struct cyclades_card *cinfo = pci_get_drvdata(pdev);
38828c2ecf20Sopenharmony_ci	unsigned int i, channel;
38838c2ecf20Sopenharmony_ci
38848c2ecf20Sopenharmony_ci	/* non-Z with old PLX */
38858c2ecf20Sopenharmony_ci	if (!cy_is_Z(cinfo) && (readb(cinfo->base_addr + CyPLX_VER) & 0x0f) ==
38868c2ecf20Sopenharmony_ci			PLX_9050)
38878c2ecf20Sopenharmony_ci		cy_writeb(cinfo->ctl_addr.p9050 + 0x4c, 0);
38888c2ecf20Sopenharmony_ci	else
38898c2ecf20Sopenharmony_ci#ifndef CONFIG_CYZ_INTR
38908c2ecf20Sopenharmony_ci		if (!cy_is_Z(cinfo))
38918c2ecf20Sopenharmony_ci#endif
38928c2ecf20Sopenharmony_ci		cy_writew(&cinfo->ctl_addr.p9060->intr_ctrl_stat,
38938c2ecf20Sopenharmony_ci			readw(&cinfo->ctl_addr.p9060->intr_ctrl_stat) &
38948c2ecf20Sopenharmony_ci			~0x0900);
38958c2ecf20Sopenharmony_ci
38968c2ecf20Sopenharmony_ci	iounmap(cinfo->base_addr);
38978c2ecf20Sopenharmony_ci	if (cinfo->ctl_addr.p9050)
38988c2ecf20Sopenharmony_ci		iounmap(cinfo->ctl_addr.p9050);
38998c2ecf20Sopenharmony_ci	if (cinfo->irq
39008c2ecf20Sopenharmony_ci#ifndef CONFIG_CYZ_INTR
39018c2ecf20Sopenharmony_ci		&& !cy_is_Z(cinfo)
39028c2ecf20Sopenharmony_ci#endif /* CONFIG_CYZ_INTR */
39038c2ecf20Sopenharmony_ci		)
39048c2ecf20Sopenharmony_ci		free_irq(cinfo->irq, cinfo);
39058c2ecf20Sopenharmony_ci	pci_release_regions(pdev);
39068c2ecf20Sopenharmony_ci
39078c2ecf20Sopenharmony_ci	cinfo->base_addr = NULL;
39088c2ecf20Sopenharmony_ci	for (channel = 0, i = cinfo->first_line; i < cinfo->first_line +
39098c2ecf20Sopenharmony_ci			cinfo->nports; i++, channel++) {
39108c2ecf20Sopenharmony_ci		tty_unregister_device(cy_serial_driver, i);
39118c2ecf20Sopenharmony_ci		tty_port_destroy(&cinfo->ports[channel].port);
39128c2ecf20Sopenharmony_ci	}
39138c2ecf20Sopenharmony_ci	cinfo->nports = 0;
39148c2ecf20Sopenharmony_ci	kfree(cinfo->ports);
39158c2ecf20Sopenharmony_ci}
39168c2ecf20Sopenharmony_ci
39178c2ecf20Sopenharmony_cistatic struct pci_driver cy_pci_driver = {
39188c2ecf20Sopenharmony_ci	.name = "cyclades",
39198c2ecf20Sopenharmony_ci	.id_table = cy_pci_dev_id,
39208c2ecf20Sopenharmony_ci	.probe = cy_pci_probe,
39218c2ecf20Sopenharmony_ci	.remove = cy_pci_remove
39228c2ecf20Sopenharmony_ci};
39238c2ecf20Sopenharmony_ci#endif
39248c2ecf20Sopenharmony_ci
39258c2ecf20Sopenharmony_cistatic int cyclades_proc_show(struct seq_file *m, void *v)
39268c2ecf20Sopenharmony_ci{
39278c2ecf20Sopenharmony_ci	struct cyclades_port *info;
39288c2ecf20Sopenharmony_ci	unsigned int i, j;
39298c2ecf20Sopenharmony_ci	__u32 cur_jifs = jiffies;
39308c2ecf20Sopenharmony_ci
39318c2ecf20Sopenharmony_ci	seq_puts(m, "Dev TimeOpen   BytesOut  IdleOut    BytesIn   "
39328c2ecf20Sopenharmony_ci			"IdleIn  Overruns  Ldisc\n");
39338c2ecf20Sopenharmony_ci
39348c2ecf20Sopenharmony_ci	/* Output one line for each known port */
39358c2ecf20Sopenharmony_ci	for (i = 0; i < NR_CARDS; i++)
39368c2ecf20Sopenharmony_ci		for (j = 0; j < cy_card[i].nports; j++) {
39378c2ecf20Sopenharmony_ci			info = &cy_card[i].ports[j];
39388c2ecf20Sopenharmony_ci
39398c2ecf20Sopenharmony_ci			if (info->port.count) {
39408c2ecf20Sopenharmony_ci				/* XXX is the ldisc num worth this? */
39418c2ecf20Sopenharmony_ci				struct tty_struct *tty;
39428c2ecf20Sopenharmony_ci				struct tty_ldisc *ld;
39438c2ecf20Sopenharmony_ci				int num = 0;
39448c2ecf20Sopenharmony_ci				tty = tty_port_tty_get(&info->port);
39458c2ecf20Sopenharmony_ci				if (tty) {
39468c2ecf20Sopenharmony_ci					ld = tty_ldisc_ref(tty);
39478c2ecf20Sopenharmony_ci					if (ld) {
39488c2ecf20Sopenharmony_ci						num = ld->ops->num;
39498c2ecf20Sopenharmony_ci						tty_ldisc_deref(ld);
39508c2ecf20Sopenharmony_ci					}
39518c2ecf20Sopenharmony_ci					tty_kref_put(tty);
39528c2ecf20Sopenharmony_ci				}
39538c2ecf20Sopenharmony_ci				seq_printf(m, "%3d %8lu %10lu %8lu "
39548c2ecf20Sopenharmony_ci					"%10lu %8lu %9lu %6d\n", info->line,
39558c2ecf20Sopenharmony_ci					(cur_jifs - info->idle_stats.in_use) /
39568c2ecf20Sopenharmony_ci					HZ, info->idle_stats.xmit_bytes,
39578c2ecf20Sopenharmony_ci					(cur_jifs - info->idle_stats.xmit_idle)/
39588c2ecf20Sopenharmony_ci					HZ, info->idle_stats.recv_bytes,
39598c2ecf20Sopenharmony_ci					(cur_jifs - info->idle_stats.recv_idle)/
39608c2ecf20Sopenharmony_ci					HZ, info->idle_stats.overruns,
39618c2ecf20Sopenharmony_ci					num);
39628c2ecf20Sopenharmony_ci			} else
39638c2ecf20Sopenharmony_ci				seq_printf(m, "%3d %8lu %10lu %8lu "
39648c2ecf20Sopenharmony_ci					"%10lu %8lu %9lu %6ld\n",
39658c2ecf20Sopenharmony_ci					info->line, 0L, 0L, 0L, 0L, 0L, 0L, 0L);
39668c2ecf20Sopenharmony_ci		}
39678c2ecf20Sopenharmony_ci	return 0;
39688c2ecf20Sopenharmony_ci}
39698c2ecf20Sopenharmony_ci
39708c2ecf20Sopenharmony_ci/* The serial driver boot-time initialization code!
39718c2ecf20Sopenharmony_ci    Hardware I/O ports are mapped to character special devices on a
39728c2ecf20Sopenharmony_ci    first found, first allocated manner.  That is, this code searches
39738c2ecf20Sopenharmony_ci    for Cyclom cards in the system.  As each is found, it is probed
39748c2ecf20Sopenharmony_ci    to discover how many chips (and thus how many ports) are present.
39758c2ecf20Sopenharmony_ci    These ports are mapped to the tty ports 32 and upward in monotonic
39768c2ecf20Sopenharmony_ci    fashion.  If an 8-port card is replaced with a 16-port card, the
39778c2ecf20Sopenharmony_ci    port mapping on a following card will shift.
39788c2ecf20Sopenharmony_ci
39798c2ecf20Sopenharmony_ci    This approach is different from what is used in the other serial
39808c2ecf20Sopenharmony_ci    device driver because the Cyclom is more properly a multiplexer,
39818c2ecf20Sopenharmony_ci    not just an aggregation of serial ports on one card.
39828c2ecf20Sopenharmony_ci
39838c2ecf20Sopenharmony_ci    If there are more cards with more ports than have been
39848c2ecf20Sopenharmony_ci    statically allocated above, a warning is printed and the
39858c2ecf20Sopenharmony_ci    extra ports are ignored.
39868c2ecf20Sopenharmony_ci */
39878c2ecf20Sopenharmony_ci
39888c2ecf20Sopenharmony_cistatic const struct tty_operations cy_ops = {
39898c2ecf20Sopenharmony_ci	.open = cy_open,
39908c2ecf20Sopenharmony_ci	.close = cy_close,
39918c2ecf20Sopenharmony_ci	.write = cy_write,
39928c2ecf20Sopenharmony_ci	.put_char = cy_put_char,
39938c2ecf20Sopenharmony_ci	.flush_chars = cy_flush_chars,
39948c2ecf20Sopenharmony_ci	.write_room = cy_write_room,
39958c2ecf20Sopenharmony_ci	.chars_in_buffer = cy_chars_in_buffer,
39968c2ecf20Sopenharmony_ci	.flush_buffer = cy_flush_buffer,
39978c2ecf20Sopenharmony_ci	.ioctl = cy_ioctl,
39988c2ecf20Sopenharmony_ci	.throttle = cy_throttle,
39998c2ecf20Sopenharmony_ci	.unthrottle = cy_unthrottle,
40008c2ecf20Sopenharmony_ci	.set_termios = cy_set_termios,
40018c2ecf20Sopenharmony_ci	.stop = cy_stop,
40028c2ecf20Sopenharmony_ci	.start = cy_start,
40038c2ecf20Sopenharmony_ci	.hangup = cy_hangup,
40048c2ecf20Sopenharmony_ci	.break_ctl = cy_break,
40058c2ecf20Sopenharmony_ci	.wait_until_sent = cy_wait_until_sent,
40068c2ecf20Sopenharmony_ci	.tiocmget = cy_tiocmget,
40078c2ecf20Sopenharmony_ci	.tiocmset = cy_tiocmset,
40088c2ecf20Sopenharmony_ci	.get_icount = cy_get_icount,
40098c2ecf20Sopenharmony_ci	.set_serial = cy_set_serial_info,
40108c2ecf20Sopenharmony_ci	.get_serial = cy_get_serial_info,
40118c2ecf20Sopenharmony_ci	.proc_show = cyclades_proc_show,
40128c2ecf20Sopenharmony_ci};
40138c2ecf20Sopenharmony_ci
40148c2ecf20Sopenharmony_cistatic int __init cy_init(void)
40158c2ecf20Sopenharmony_ci{
40168c2ecf20Sopenharmony_ci	unsigned int nboards;
40178c2ecf20Sopenharmony_ci	int retval = -ENOMEM;
40188c2ecf20Sopenharmony_ci
40198c2ecf20Sopenharmony_ci	cy_serial_driver = alloc_tty_driver(NR_PORTS);
40208c2ecf20Sopenharmony_ci	if (!cy_serial_driver)
40218c2ecf20Sopenharmony_ci		goto err;
40228c2ecf20Sopenharmony_ci
40238c2ecf20Sopenharmony_ci	printk(KERN_INFO "Cyclades driver " CY_VERSION "\n");
40248c2ecf20Sopenharmony_ci
40258c2ecf20Sopenharmony_ci	/* Initialize the tty_driver structure */
40268c2ecf20Sopenharmony_ci
40278c2ecf20Sopenharmony_ci	cy_serial_driver->driver_name = "cyclades";
40288c2ecf20Sopenharmony_ci	cy_serial_driver->name = "ttyC";
40298c2ecf20Sopenharmony_ci	cy_serial_driver->major = CYCLADES_MAJOR;
40308c2ecf20Sopenharmony_ci	cy_serial_driver->minor_start = 0;
40318c2ecf20Sopenharmony_ci	cy_serial_driver->type = TTY_DRIVER_TYPE_SERIAL;
40328c2ecf20Sopenharmony_ci	cy_serial_driver->subtype = SERIAL_TYPE_NORMAL;
40338c2ecf20Sopenharmony_ci	cy_serial_driver->init_termios = tty_std_termios;
40348c2ecf20Sopenharmony_ci	cy_serial_driver->init_termios.c_cflag =
40358c2ecf20Sopenharmony_ci	    B9600 | CS8 | CREAD | HUPCL | CLOCAL;
40368c2ecf20Sopenharmony_ci	cy_serial_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
40378c2ecf20Sopenharmony_ci	tty_set_operations(cy_serial_driver, &cy_ops);
40388c2ecf20Sopenharmony_ci
40398c2ecf20Sopenharmony_ci	retval = tty_register_driver(cy_serial_driver);
40408c2ecf20Sopenharmony_ci	if (retval) {
40418c2ecf20Sopenharmony_ci		printk(KERN_ERR "Couldn't register Cyclades serial driver\n");
40428c2ecf20Sopenharmony_ci		goto err_frtty;
40438c2ecf20Sopenharmony_ci	}
40448c2ecf20Sopenharmony_ci
40458c2ecf20Sopenharmony_ci	/* the code below is responsible to find the boards. Each different
40468c2ecf20Sopenharmony_ci	   type of board has its own detection routine. If a board is found,
40478c2ecf20Sopenharmony_ci	   the next cy_card structure available is set by the detection
40488c2ecf20Sopenharmony_ci	   routine. These functions are responsible for checking the
40498c2ecf20Sopenharmony_ci	   availability of cy_card and cy_port data structures and updating
40508c2ecf20Sopenharmony_ci	   the cy_next_channel. */
40518c2ecf20Sopenharmony_ci
40528c2ecf20Sopenharmony_ci	/* look for isa boards */
40538c2ecf20Sopenharmony_ci	nboards = cy_detect_isa();
40548c2ecf20Sopenharmony_ci
40558c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI
40568c2ecf20Sopenharmony_ci	/* look for pci boards */
40578c2ecf20Sopenharmony_ci	retval = pci_register_driver(&cy_pci_driver);
40588c2ecf20Sopenharmony_ci	if (retval && !nboards) {
40598c2ecf20Sopenharmony_ci		tty_unregister_driver(cy_serial_driver);
40608c2ecf20Sopenharmony_ci		goto err_frtty;
40618c2ecf20Sopenharmony_ci	}
40628c2ecf20Sopenharmony_ci#endif
40638c2ecf20Sopenharmony_ci
40648c2ecf20Sopenharmony_ci	return 0;
40658c2ecf20Sopenharmony_cierr_frtty:
40668c2ecf20Sopenharmony_ci	put_tty_driver(cy_serial_driver);
40678c2ecf20Sopenharmony_cierr:
40688c2ecf20Sopenharmony_ci	return retval;
40698c2ecf20Sopenharmony_ci}				/* cy_init */
40708c2ecf20Sopenharmony_ci
40718c2ecf20Sopenharmony_cistatic void __exit cy_cleanup_module(void)
40728c2ecf20Sopenharmony_ci{
40738c2ecf20Sopenharmony_ci	struct cyclades_card *card;
40748c2ecf20Sopenharmony_ci	unsigned int i, e1;
40758c2ecf20Sopenharmony_ci
40768c2ecf20Sopenharmony_ci#ifndef CONFIG_CYZ_INTR
40778c2ecf20Sopenharmony_ci	del_timer_sync(&cyz_timerlist);
40788c2ecf20Sopenharmony_ci#endif /* CONFIG_CYZ_INTR */
40798c2ecf20Sopenharmony_ci
40808c2ecf20Sopenharmony_ci	e1 = tty_unregister_driver(cy_serial_driver);
40818c2ecf20Sopenharmony_ci	if (e1)
40828c2ecf20Sopenharmony_ci		printk(KERN_ERR "failed to unregister Cyclades serial "
40838c2ecf20Sopenharmony_ci				"driver(%d)\n", e1);
40848c2ecf20Sopenharmony_ci
40858c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI
40868c2ecf20Sopenharmony_ci	pci_unregister_driver(&cy_pci_driver);
40878c2ecf20Sopenharmony_ci#endif
40888c2ecf20Sopenharmony_ci
40898c2ecf20Sopenharmony_ci	for (i = 0; i < NR_CARDS; i++) {
40908c2ecf20Sopenharmony_ci		card = &cy_card[i];
40918c2ecf20Sopenharmony_ci		if (card->base_addr) {
40928c2ecf20Sopenharmony_ci			/* clear interrupt */
40938c2ecf20Sopenharmony_ci			cy_writeb(card->base_addr + Cy_ClrIntr, 0);
40948c2ecf20Sopenharmony_ci			iounmap(card->base_addr);
40958c2ecf20Sopenharmony_ci			if (card->ctl_addr.p9050)
40968c2ecf20Sopenharmony_ci				iounmap(card->ctl_addr.p9050);
40978c2ecf20Sopenharmony_ci			if (card->irq
40988c2ecf20Sopenharmony_ci#ifndef CONFIG_CYZ_INTR
40998c2ecf20Sopenharmony_ci				&& !cy_is_Z(card)
41008c2ecf20Sopenharmony_ci#endif /* CONFIG_CYZ_INTR */
41018c2ecf20Sopenharmony_ci				)
41028c2ecf20Sopenharmony_ci				free_irq(card->irq, card);
41038c2ecf20Sopenharmony_ci			for (e1 = card->first_line; e1 < card->first_line +
41048c2ecf20Sopenharmony_ci					card->nports; e1++)
41058c2ecf20Sopenharmony_ci				tty_unregister_device(cy_serial_driver, e1);
41068c2ecf20Sopenharmony_ci			kfree(card->ports);
41078c2ecf20Sopenharmony_ci		}
41088c2ecf20Sopenharmony_ci	}
41098c2ecf20Sopenharmony_ci
41108c2ecf20Sopenharmony_ci	put_tty_driver(cy_serial_driver);
41118c2ecf20Sopenharmony_ci} /* cy_cleanup_module */
41128c2ecf20Sopenharmony_ci
41138c2ecf20Sopenharmony_cimodule_init(cy_init);
41148c2ecf20Sopenharmony_cimodule_exit(cy_cleanup_module);
41158c2ecf20Sopenharmony_ci
41168c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
41178c2ecf20Sopenharmony_ciMODULE_VERSION(CY_VERSION);
41188c2ecf20Sopenharmony_ciMODULE_ALIAS_CHARDEV_MAJOR(CYCLADES_MAJOR);
41198c2ecf20Sopenharmony_ciMODULE_FIRMWARE("cyzfirm.bin");
4120