162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-1.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Device driver for Microgate SyncLink GT serial adapters.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * written by Paul Fulghum for Microgate Corporation
662306a36Sopenharmony_ci * paulkf@microgate.com
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Microgate and SyncLink are trademarks of Microgate Corporation
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
1162306a36Sopenharmony_ci * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1262306a36Sopenharmony_ci * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
1362306a36Sopenharmony_ci * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
1462306a36Sopenharmony_ci * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
1562306a36Sopenharmony_ci * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
1662306a36Sopenharmony_ci * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
1762306a36Sopenharmony_ci * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
1862306a36Sopenharmony_ci * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
1962306a36Sopenharmony_ci * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
2062306a36Sopenharmony_ci * OF THE POSSIBILITY OF SUCH DAMAGE.
2162306a36Sopenharmony_ci */
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/*
2462306a36Sopenharmony_ci * DEBUG OUTPUT DEFINITIONS
2562306a36Sopenharmony_ci *
2662306a36Sopenharmony_ci * uncomment lines below to enable specific types of debug output
2762306a36Sopenharmony_ci *
2862306a36Sopenharmony_ci * DBGINFO   information - most verbose output
2962306a36Sopenharmony_ci * DBGERR    serious errors
3062306a36Sopenharmony_ci * DBGBH     bottom half service routine debugging
3162306a36Sopenharmony_ci * DBGISR    interrupt service routine debugging
3262306a36Sopenharmony_ci * DBGDATA   output receive and transmit data
3362306a36Sopenharmony_ci * DBGTBUF   output transmit DMA buffers and registers
3462306a36Sopenharmony_ci * DBGRBUF   output receive DMA buffers and registers
3562306a36Sopenharmony_ci */
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define DBGINFO(fmt) if (debug_level >= DEBUG_LEVEL_INFO) printk fmt
3862306a36Sopenharmony_ci#define DBGERR(fmt) if (debug_level >= DEBUG_LEVEL_ERROR) printk fmt
3962306a36Sopenharmony_ci#define DBGBH(fmt) if (debug_level >= DEBUG_LEVEL_BH) printk fmt
4062306a36Sopenharmony_ci#define DBGISR(fmt) if (debug_level >= DEBUG_LEVEL_ISR) printk fmt
4162306a36Sopenharmony_ci#define DBGDATA(info, buf, size, label) if (debug_level >= DEBUG_LEVEL_DATA) trace_block((info), (buf), (size), (label))
4262306a36Sopenharmony_ci/*#define DBGTBUF(info) dump_tbufs(info)*/
4362306a36Sopenharmony_ci/*#define DBGRBUF(info) dump_rbufs(info)*/
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#include <linux/module.h>
4762306a36Sopenharmony_ci#include <linux/errno.h>
4862306a36Sopenharmony_ci#include <linux/signal.h>
4962306a36Sopenharmony_ci#include <linux/sched.h>
5062306a36Sopenharmony_ci#include <linux/timer.h>
5162306a36Sopenharmony_ci#include <linux/interrupt.h>
5262306a36Sopenharmony_ci#include <linux/pci.h>
5362306a36Sopenharmony_ci#include <linux/tty.h>
5462306a36Sopenharmony_ci#include <linux/tty_flip.h>
5562306a36Sopenharmony_ci#include <linux/serial.h>
5662306a36Sopenharmony_ci#include <linux/major.h>
5762306a36Sopenharmony_ci#include <linux/string.h>
5862306a36Sopenharmony_ci#include <linux/fcntl.h>
5962306a36Sopenharmony_ci#include <linux/ptrace.h>
6062306a36Sopenharmony_ci#include <linux/ioport.h>
6162306a36Sopenharmony_ci#include <linux/mm.h>
6262306a36Sopenharmony_ci#include <linux/seq_file.h>
6362306a36Sopenharmony_ci#include <linux/slab.h>
6462306a36Sopenharmony_ci#include <linux/netdevice.h>
6562306a36Sopenharmony_ci#include <linux/vmalloc.h>
6662306a36Sopenharmony_ci#include <linux/init.h>
6762306a36Sopenharmony_ci#include <linux/delay.h>
6862306a36Sopenharmony_ci#include <linux/ioctl.h>
6962306a36Sopenharmony_ci#include <linux/termios.h>
7062306a36Sopenharmony_ci#include <linux/bitops.h>
7162306a36Sopenharmony_ci#include <linux/workqueue.h>
7262306a36Sopenharmony_ci#include <linux/hdlc.h>
7362306a36Sopenharmony_ci#include <linux/synclink.h>
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci#include <asm/io.h>
7662306a36Sopenharmony_ci#include <asm/irq.h>
7762306a36Sopenharmony_ci#include <asm/dma.h>
7862306a36Sopenharmony_ci#include <asm/types.h>
7962306a36Sopenharmony_ci#include <linux/uaccess.h>
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci#if defined(CONFIG_HDLC) || (defined(CONFIG_HDLC_MODULE) && defined(CONFIG_SYNCLINK_GT_MODULE))
8262306a36Sopenharmony_ci#define SYNCLINK_GENERIC_HDLC 1
8362306a36Sopenharmony_ci#else
8462306a36Sopenharmony_ci#define SYNCLINK_GENERIC_HDLC 0
8562306a36Sopenharmony_ci#endif
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci/*
8862306a36Sopenharmony_ci * module identification
8962306a36Sopenharmony_ci */
9062306a36Sopenharmony_cistatic const char driver_name[] = "SyncLink GT";
9162306a36Sopenharmony_cistatic const char tty_dev_prefix[] = "ttySLG";
9262306a36Sopenharmony_ciMODULE_LICENSE("GPL");
9362306a36Sopenharmony_ci#define MAX_DEVICES 32
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic const struct pci_device_id pci_table[] = {
9662306a36Sopenharmony_ci	{ PCI_VDEVICE(MICROGATE, SYNCLINK_GT_DEVICE_ID) },
9762306a36Sopenharmony_ci	{ PCI_VDEVICE(MICROGATE, SYNCLINK_GT2_DEVICE_ID) },
9862306a36Sopenharmony_ci	{ PCI_VDEVICE(MICROGATE, SYNCLINK_GT4_DEVICE_ID) },
9962306a36Sopenharmony_ci	{ PCI_VDEVICE(MICROGATE, SYNCLINK_AC_DEVICE_ID) },
10062306a36Sopenharmony_ci	{ 0 }, /* terminate list */
10162306a36Sopenharmony_ci};
10262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pci_table);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic int  init_one(struct pci_dev *dev,const struct pci_device_id *ent);
10562306a36Sopenharmony_cistatic void remove_one(struct pci_dev *dev);
10662306a36Sopenharmony_cistatic struct pci_driver pci_driver = {
10762306a36Sopenharmony_ci	.name		= "synclink_gt",
10862306a36Sopenharmony_ci	.id_table	= pci_table,
10962306a36Sopenharmony_ci	.probe		= init_one,
11062306a36Sopenharmony_ci	.remove		= remove_one,
11162306a36Sopenharmony_ci};
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic bool pci_registered;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci/*
11662306a36Sopenharmony_ci * module configuration and status
11762306a36Sopenharmony_ci */
11862306a36Sopenharmony_cistatic struct slgt_info *slgt_device_list;
11962306a36Sopenharmony_cistatic int slgt_device_count;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic int ttymajor;
12262306a36Sopenharmony_cistatic int debug_level;
12362306a36Sopenharmony_cistatic int maxframe[MAX_DEVICES];
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cimodule_param(ttymajor, int, 0);
12662306a36Sopenharmony_cimodule_param(debug_level, int, 0);
12762306a36Sopenharmony_cimodule_param_array(maxframe, int, NULL, 0);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ciMODULE_PARM_DESC(ttymajor, "TTY major device number override: 0=auto assigned");
13062306a36Sopenharmony_ciMODULE_PARM_DESC(debug_level, "Debug syslog output: 0=disabled, 1 to 5=increasing detail");
13162306a36Sopenharmony_ciMODULE_PARM_DESC(maxframe, "Maximum frame size used by device (4096 to 65535)");
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci/*
13462306a36Sopenharmony_ci * tty support and callbacks
13562306a36Sopenharmony_ci */
13662306a36Sopenharmony_cistatic struct tty_driver *serial_driver;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic void wait_until_sent(struct tty_struct *tty, int timeout);
13962306a36Sopenharmony_cistatic void flush_buffer(struct tty_struct *tty);
14062306a36Sopenharmony_cistatic void tx_release(struct tty_struct *tty);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci/*
14362306a36Sopenharmony_ci * generic HDLC support
14462306a36Sopenharmony_ci */
14562306a36Sopenharmony_ci#define dev_to_port(D) (dev_to_hdlc(D)->priv)
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci/*
14962306a36Sopenharmony_ci * device specific structures, macros and functions
15062306a36Sopenharmony_ci */
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci#define SLGT_MAX_PORTS 4
15362306a36Sopenharmony_ci#define SLGT_REG_SIZE  256
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci/*
15662306a36Sopenharmony_ci * conditional wait facility
15762306a36Sopenharmony_ci */
15862306a36Sopenharmony_cistruct cond_wait {
15962306a36Sopenharmony_ci	struct cond_wait *next;
16062306a36Sopenharmony_ci	wait_queue_head_t q;
16162306a36Sopenharmony_ci	wait_queue_entry_t wait;
16262306a36Sopenharmony_ci	unsigned int data;
16362306a36Sopenharmony_ci};
16462306a36Sopenharmony_cistatic void flush_cond_wait(struct cond_wait **head);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci/*
16762306a36Sopenharmony_ci * DMA buffer descriptor and access macros
16862306a36Sopenharmony_ci */
16962306a36Sopenharmony_cistruct slgt_desc
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	__le16 count;
17262306a36Sopenharmony_ci	__le16 status;
17362306a36Sopenharmony_ci	__le32 pbuf;  /* physical address of data buffer */
17462306a36Sopenharmony_ci	__le32 next;  /* physical address of next descriptor */
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	/* driver book keeping */
17762306a36Sopenharmony_ci	char *buf;          /* virtual  address of data buffer */
17862306a36Sopenharmony_ci    	unsigned int pdesc; /* physical address of this descriptor */
17962306a36Sopenharmony_ci	dma_addr_t buf_dma_addr;
18062306a36Sopenharmony_ci	unsigned short buf_count;
18162306a36Sopenharmony_ci};
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci#define set_desc_buffer(a,b) (a).pbuf = cpu_to_le32((unsigned int)(b))
18462306a36Sopenharmony_ci#define set_desc_next(a,b) (a).next   = cpu_to_le32((unsigned int)(b))
18562306a36Sopenharmony_ci#define set_desc_count(a,b)(a).count  = cpu_to_le16((unsigned short)(b))
18662306a36Sopenharmony_ci#define set_desc_eof(a,b)  (a).status = cpu_to_le16((b) ? (le16_to_cpu((a).status) | BIT0) : (le16_to_cpu((a).status) & ~BIT0))
18762306a36Sopenharmony_ci#define set_desc_status(a, b) (a).status = cpu_to_le16((unsigned short)(b))
18862306a36Sopenharmony_ci#define desc_count(a)      (le16_to_cpu((a).count))
18962306a36Sopenharmony_ci#define desc_status(a)     (le16_to_cpu((a).status))
19062306a36Sopenharmony_ci#define desc_complete(a)   (le16_to_cpu((a).status) & BIT15)
19162306a36Sopenharmony_ci#define desc_eof(a)        (le16_to_cpu((a).status) & BIT2)
19262306a36Sopenharmony_ci#define desc_crc_error(a)  (le16_to_cpu((a).status) & BIT1)
19362306a36Sopenharmony_ci#define desc_abort(a)      (le16_to_cpu((a).status) & BIT0)
19462306a36Sopenharmony_ci#define desc_residue(a)    ((le16_to_cpu((a).status) & 0x38) >> 3)
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistruct _input_signal_events {
19762306a36Sopenharmony_ci	int ri_up;
19862306a36Sopenharmony_ci	int ri_down;
19962306a36Sopenharmony_ci	int dsr_up;
20062306a36Sopenharmony_ci	int dsr_down;
20162306a36Sopenharmony_ci	int dcd_up;
20262306a36Sopenharmony_ci	int dcd_down;
20362306a36Sopenharmony_ci	int cts_up;
20462306a36Sopenharmony_ci	int cts_down;
20562306a36Sopenharmony_ci};
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci/*
20862306a36Sopenharmony_ci * device instance data structure
20962306a36Sopenharmony_ci */
21062306a36Sopenharmony_cistruct slgt_info {
21162306a36Sopenharmony_ci	void *if_ptr;		/* General purpose pointer (used by SPPP) */
21262306a36Sopenharmony_ci	struct tty_port port;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	struct slgt_info *next_device;	/* device list link */
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	char device_name[25];
21762306a36Sopenharmony_ci	struct pci_dev *pdev;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	int port_count;  /* count of ports on adapter */
22062306a36Sopenharmony_ci	int adapter_num; /* adapter instance number */
22162306a36Sopenharmony_ci	int port_num;    /* port instance number */
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	/* array of pointers to port contexts on this adapter */
22462306a36Sopenharmony_ci	struct slgt_info *port_array[SLGT_MAX_PORTS];
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	int			line;		/* tty line instance number */
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	struct mgsl_icount	icount;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	int			timeout;
23162306a36Sopenharmony_ci	int			x_char;		/* xon/xoff character */
23262306a36Sopenharmony_ci	unsigned int		read_status_mask;
23362306a36Sopenharmony_ci	unsigned int 		ignore_status_mask;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	wait_queue_head_t	status_event_wait_q;
23662306a36Sopenharmony_ci	wait_queue_head_t	event_wait_q;
23762306a36Sopenharmony_ci	struct timer_list	tx_timer;
23862306a36Sopenharmony_ci	struct timer_list	rx_timer;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	unsigned int            gpio_present;
24162306a36Sopenharmony_ci	struct cond_wait        *gpio_wait_q;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	spinlock_t lock;	/* spinlock for synchronizing with ISR */
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	struct work_struct task;
24662306a36Sopenharmony_ci	u32 pending_bh;
24762306a36Sopenharmony_ci	bool bh_requested;
24862306a36Sopenharmony_ci	bool bh_running;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	int isr_overflow;
25162306a36Sopenharmony_ci	bool irq_requested;	/* true if IRQ requested */
25262306a36Sopenharmony_ci	bool irq_occurred;	/* for diagnostics use */
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	/* device configuration */
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	unsigned int bus_type;
25762306a36Sopenharmony_ci	unsigned int irq_level;
25862306a36Sopenharmony_ci	unsigned long irq_flags;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	unsigned char __iomem * reg_addr;  /* memory mapped registers address */
26162306a36Sopenharmony_ci	u32 phys_reg_addr;
26262306a36Sopenharmony_ci	bool reg_addr_requested;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	MGSL_PARAMS params;       /* communications parameters */
26562306a36Sopenharmony_ci	u32 idle_mode;
26662306a36Sopenharmony_ci	u32 max_frame_size;       /* as set by device config */
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	unsigned int rbuf_fill_level;
26962306a36Sopenharmony_ci	unsigned int rx_pio;
27062306a36Sopenharmony_ci	unsigned int if_mode;
27162306a36Sopenharmony_ci	unsigned int base_clock;
27262306a36Sopenharmony_ci	unsigned int xsync;
27362306a36Sopenharmony_ci	unsigned int xctrl;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	/* device status */
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	bool rx_enabled;
27862306a36Sopenharmony_ci	bool rx_restart;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	bool tx_enabled;
28162306a36Sopenharmony_ci	bool tx_active;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	unsigned char signals;    /* serial signal states */
28462306a36Sopenharmony_ci	int init_error;  /* initialization error */
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	unsigned char *tx_buf;
28762306a36Sopenharmony_ci	int tx_count;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	bool drop_rts_on_tx_done;
29062306a36Sopenharmony_ci	struct	_input_signal_events	input_signal_events;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	int dcd_chkcount;	/* check counts to prevent */
29362306a36Sopenharmony_ci	int cts_chkcount;	/* too many IRQs if a signal */
29462306a36Sopenharmony_ci	int dsr_chkcount;	/* is floating */
29562306a36Sopenharmony_ci	int ri_chkcount;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	char *bufs;		/* virtual address of DMA buffer lists */
29862306a36Sopenharmony_ci	dma_addr_t bufs_dma_addr; /* physical address of buffer descriptors */
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	unsigned int rbuf_count;
30162306a36Sopenharmony_ci	struct slgt_desc *rbufs;
30262306a36Sopenharmony_ci	unsigned int rbuf_current;
30362306a36Sopenharmony_ci	unsigned int rbuf_index;
30462306a36Sopenharmony_ci	unsigned int rbuf_fill_index;
30562306a36Sopenharmony_ci	unsigned short rbuf_fill_count;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	unsigned int tbuf_count;
30862306a36Sopenharmony_ci	struct slgt_desc *tbufs;
30962306a36Sopenharmony_ci	unsigned int tbuf_current;
31062306a36Sopenharmony_ci	unsigned int tbuf_start;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	unsigned char *tmp_rbuf;
31362306a36Sopenharmony_ci	unsigned int tmp_rbuf_count;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	/* SPPP/Cisco HDLC device parts */
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	int netcount;
31862306a36Sopenharmony_ci	spinlock_t netlock;
31962306a36Sopenharmony_ci#if SYNCLINK_GENERIC_HDLC
32062306a36Sopenharmony_ci	struct net_device *netdev;
32162306a36Sopenharmony_ci#endif
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci};
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_cistatic const MGSL_PARAMS default_params = {
32662306a36Sopenharmony_ci	.mode            = MGSL_MODE_HDLC,
32762306a36Sopenharmony_ci	.loopback        = 0,
32862306a36Sopenharmony_ci	.flags           = HDLC_FLAG_UNDERRUN_ABORT15,
32962306a36Sopenharmony_ci	.encoding        = HDLC_ENCODING_NRZI_SPACE,
33062306a36Sopenharmony_ci	.clock_speed     = 0,
33162306a36Sopenharmony_ci	.addr_filter     = 0xff,
33262306a36Sopenharmony_ci	.crc_type        = HDLC_CRC_16_CCITT,
33362306a36Sopenharmony_ci	.preamble_length = HDLC_PREAMBLE_LENGTH_8BITS,
33462306a36Sopenharmony_ci	.preamble        = HDLC_PREAMBLE_PATTERN_NONE,
33562306a36Sopenharmony_ci	.data_rate       = 9600,
33662306a36Sopenharmony_ci	.data_bits       = 8,
33762306a36Sopenharmony_ci	.stop_bits       = 1,
33862306a36Sopenharmony_ci	.parity          = ASYNC_PARITY_NONE
33962306a36Sopenharmony_ci};
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci#define BH_RECEIVE  1
34362306a36Sopenharmony_ci#define BH_TRANSMIT 2
34462306a36Sopenharmony_ci#define BH_STATUS   4
34562306a36Sopenharmony_ci#define IO_PIN_SHUTDOWN_LIMIT 100
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci#define DMABUFSIZE 256
34862306a36Sopenharmony_ci#define DESC_LIST_SIZE 4096
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci#define MASK_PARITY  BIT1
35162306a36Sopenharmony_ci#define MASK_FRAMING BIT0
35262306a36Sopenharmony_ci#define MASK_BREAK   BIT14
35362306a36Sopenharmony_ci#define MASK_OVERRUN BIT4
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci#define GSR   0x00 /* global status */
35662306a36Sopenharmony_ci#define JCR   0x04 /* JTAG control */
35762306a36Sopenharmony_ci#define IODR  0x08 /* GPIO direction */
35862306a36Sopenharmony_ci#define IOER  0x0c /* GPIO interrupt enable */
35962306a36Sopenharmony_ci#define IOVR  0x10 /* GPIO value */
36062306a36Sopenharmony_ci#define IOSR  0x14 /* GPIO interrupt status */
36162306a36Sopenharmony_ci#define TDR   0x80 /* tx data */
36262306a36Sopenharmony_ci#define RDR   0x80 /* rx data */
36362306a36Sopenharmony_ci#define TCR   0x82 /* tx control */
36462306a36Sopenharmony_ci#define TIR   0x84 /* tx idle */
36562306a36Sopenharmony_ci#define TPR   0x85 /* tx preamble */
36662306a36Sopenharmony_ci#define RCR   0x86 /* rx control */
36762306a36Sopenharmony_ci#define VCR   0x88 /* V.24 control */
36862306a36Sopenharmony_ci#define CCR   0x89 /* clock control */
36962306a36Sopenharmony_ci#define BDR   0x8a /* baud divisor */
37062306a36Sopenharmony_ci#define SCR   0x8c /* serial control */
37162306a36Sopenharmony_ci#define SSR   0x8e /* serial status */
37262306a36Sopenharmony_ci#define RDCSR 0x90 /* rx DMA control/status */
37362306a36Sopenharmony_ci#define TDCSR 0x94 /* tx DMA control/status */
37462306a36Sopenharmony_ci#define RDDAR 0x98 /* rx DMA descriptor address */
37562306a36Sopenharmony_ci#define TDDAR 0x9c /* tx DMA descriptor address */
37662306a36Sopenharmony_ci#define XSR   0x40 /* extended sync pattern */
37762306a36Sopenharmony_ci#define XCR   0x44 /* extended control */
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci#define RXIDLE      BIT14
38062306a36Sopenharmony_ci#define RXBREAK     BIT14
38162306a36Sopenharmony_ci#define IRQ_TXDATA  BIT13
38262306a36Sopenharmony_ci#define IRQ_TXIDLE  BIT12
38362306a36Sopenharmony_ci#define IRQ_TXUNDER BIT11 /* HDLC */
38462306a36Sopenharmony_ci#define IRQ_RXDATA  BIT10
38562306a36Sopenharmony_ci#define IRQ_RXIDLE  BIT9  /* HDLC */
38662306a36Sopenharmony_ci#define IRQ_RXBREAK BIT9  /* async */
38762306a36Sopenharmony_ci#define IRQ_RXOVER  BIT8
38862306a36Sopenharmony_ci#define IRQ_DSR     BIT7
38962306a36Sopenharmony_ci#define IRQ_CTS     BIT6
39062306a36Sopenharmony_ci#define IRQ_DCD     BIT5
39162306a36Sopenharmony_ci#define IRQ_RI      BIT4
39262306a36Sopenharmony_ci#define IRQ_ALL     0x3ff0
39362306a36Sopenharmony_ci#define IRQ_MASTER  BIT0
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci#define slgt_irq_on(info, mask) \
39662306a36Sopenharmony_ci	wr_reg16((info), SCR, (unsigned short)(rd_reg16((info), SCR) | (mask)))
39762306a36Sopenharmony_ci#define slgt_irq_off(info, mask) \
39862306a36Sopenharmony_ci	wr_reg16((info), SCR, (unsigned short)(rd_reg16((info), SCR) & ~(mask)))
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_cistatic __u8  rd_reg8(struct slgt_info *info, unsigned int addr);
40162306a36Sopenharmony_cistatic void  wr_reg8(struct slgt_info *info, unsigned int addr, __u8 value);
40262306a36Sopenharmony_cistatic __u16 rd_reg16(struct slgt_info *info, unsigned int addr);
40362306a36Sopenharmony_cistatic void  wr_reg16(struct slgt_info *info, unsigned int addr, __u16 value);
40462306a36Sopenharmony_cistatic __u32 rd_reg32(struct slgt_info *info, unsigned int addr);
40562306a36Sopenharmony_cistatic void  wr_reg32(struct slgt_info *info, unsigned int addr, __u32 value);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_cistatic void  msc_set_vcr(struct slgt_info *info);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_cistatic int  startup(struct slgt_info *info);
41062306a36Sopenharmony_cistatic int  block_til_ready(struct tty_struct *tty, struct file * filp,struct slgt_info *info);
41162306a36Sopenharmony_cistatic void shutdown(struct slgt_info *info);
41262306a36Sopenharmony_cistatic void program_hw(struct slgt_info *info);
41362306a36Sopenharmony_cistatic void change_params(struct slgt_info *info);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_cistatic int  adapter_test(struct slgt_info *info);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_cistatic void reset_port(struct slgt_info *info);
41862306a36Sopenharmony_cistatic void async_mode(struct slgt_info *info);
41962306a36Sopenharmony_cistatic void sync_mode(struct slgt_info *info);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_cistatic void rx_stop(struct slgt_info *info);
42262306a36Sopenharmony_cistatic void rx_start(struct slgt_info *info);
42362306a36Sopenharmony_cistatic void reset_rbufs(struct slgt_info *info);
42462306a36Sopenharmony_cistatic void free_rbufs(struct slgt_info *info, unsigned int first, unsigned int last);
42562306a36Sopenharmony_cistatic bool rx_get_frame(struct slgt_info *info);
42662306a36Sopenharmony_cistatic bool rx_get_buf(struct slgt_info *info);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_cistatic void tx_start(struct slgt_info *info);
42962306a36Sopenharmony_cistatic void tx_stop(struct slgt_info *info);
43062306a36Sopenharmony_cistatic void tx_set_idle(struct slgt_info *info);
43162306a36Sopenharmony_cistatic unsigned int tbuf_bytes(struct slgt_info *info);
43262306a36Sopenharmony_cistatic void reset_tbufs(struct slgt_info *info);
43362306a36Sopenharmony_cistatic void tdma_reset(struct slgt_info *info);
43462306a36Sopenharmony_cistatic bool tx_load(struct slgt_info *info, const u8 *buf, unsigned int count);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_cistatic void get_gtsignals(struct slgt_info *info);
43762306a36Sopenharmony_cistatic void set_gtsignals(struct slgt_info *info);
43862306a36Sopenharmony_cistatic void set_rate(struct slgt_info *info, u32 data_rate);
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_cistatic void bh_transmit(struct slgt_info *info);
44162306a36Sopenharmony_cistatic void isr_txeom(struct slgt_info *info, unsigned short status);
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_cistatic void tx_timeout(struct timer_list *t);
44462306a36Sopenharmony_cistatic void rx_timeout(struct timer_list *t);
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci/*
44762306a36Sopenharmony_ci * ioctl handlers
44862306a36Sopenharmony_ci */
44962306a36Sopenharmony_cistatic int  get_stats(struct slgt_info *info, struct mgsl_icount __user *user_icount);
45062306a36Sopenharmony_cistatic int  get_params(struct slgt_info *info, MGSL_PARAMS __user *params);
45162306a36Sopenharmony_cistatic int  set_params(struct slgt_info *info, MGSL_PARAMS __user *params);
45262306a36Sopenharmony_cistatic int  get_txidle(struct slgt_info *info, int __user *idle_mode);
45362306a36Sopenharmony_cistatic int  set_txidle(struct slgt_info *info, int idle_mode);
45462306a36Sopenharmony_cistatic int  tx_enable(struct slgt_info *info, int enable);
45562306a36Sopenharmony_cistatic int  tx_abort(struct slgt_info *info);
45662306a36Sopenharmony_cistatic int  rx_enable(struct slgt_info *info, int enable);
45762306a36Sopenharmony_cistatic int  modem_input_wait(struct slgt_info *info,int arg);
45862306a36Sopenharmony_cistatic int  wait_mgsl_event(struct slgt_info *info, int __user *mask_ptr);
45962306a36Sopenharmony_cistatic int  get_interface(struct slgt_info *info, int __user *if_mode);
46062306a36Sopenharmony_cistatic int  set_interface(struct slgt_info *info, int if_mode);
46162306a36Sopenharmony_cistatic int  set_gpio(struct slgt_info *info, struct gpio_desc __user *gpio);
46262306a36Sopenharmony_cistatic int  get_gpio(struct slgt_info *info, struct gpio_desc __user *gpio);
46362306a36Sopenharmony_cistatic int  wait_gpio(struct slgt_info *info, struct gpio_desc __user *gpio);
46462306a36Sopenharmony_cistatic int  get_xsync(struct slgt_info *info, int __user *if_mode);
46562306a36Sopenharmony_cistatic int  set_xsync(struct slgt_info *info, int if_mode);
46662306a36Sopenharmony_cistatic int  get_xctrl(struct slgt_info *info, int __user *if_mode);
46762306a36Sopenharmony_cistatic int  set_xctrl(struct slgt_info *info, int if_mode);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci/*
47062306a36Sopenharmony_ci * driver functions
47162306a36Sopenharmony_ci */
47262306a36Sopenharmony_cistatic void release_resources(struct slgt_info *info);
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci/*
47562306a36Sopenharmony_ci * DEBUG OUTPUT CODE
47662306a36Sopenharmony_ci */
47762306a36Sopenharmony_ci#ifndef DBGINFO
47862306a36Sopenharmony_ci#define DBGINFO(fmt)
47962306a36Sopenharmony_ci#endif
48062306a36Sopenharmony_ci#ifndef DBGERR
48162306a36Sopenharmony_ci#define DBGERR(fmt)
48262306a36Sopenharmony_ci#endif
48362306a36Sopenharmony_ci#ifndef DBGBH
48462306a36Sopenharmony_ci#define DBGBH(fmt)
48562306a36Sopenharmony_ci#endif
48662306a36Sopenharmony_ci#ifndef DBGISR
48762306a36Sopenharmony_ci#define DBGISR(fmt)
48862306a36Sopenharmony_ci#endif
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci#ifdef DBGDATA
49162306a36Sopenharmony_cistatic void trace_block(struct slgt_info *info, const char *data, int count, const char *label)
49262306a36Sopenharmony_ci{
49362306a36Sopenharmony_ci	int i;
49462306a36Sopenharmony_ci	int linecount;
49562306a36Sopenharmony_ci	printk("%s %s data:\n",info->device_name, label);
49662306a36Sopenharmony_ci	while(count) {
49762306a36Sopenharmony_ci		linecount = (count > 16) ? 16 : count;
49862306a36Sopenharmony_ci		for(i=0; i < linecount; i++)
49962306a36Sopenharmony_ci			printk("%02X ",(unsigned char)data[i]);
50062306a36Sopenharmony_ci		for(;i<17;i++)
50162306a36Sopenharmony_ci			printk("   ");
50262306a36Sopenharmony_ci		for(i=0;i<linecount;i++) {
50362306a36Sopenharmony_ci			if (data[i]>=040 && data[i]<=0176)
50462306a36Sopenharmony_ci				printk("%c",data[i]);
50562306a36Sopenharmony_ci			else
50662306a36Sopenharmony_ci				printk(".");
50762306a36Sopenharmony_ci		}
50862306a36Sopenharmony_ci		printk("\n");
50962306a36Sopenharmony_ci		data  += linecount;
51062306a36Sopenharmony_ci		count -= linecount;
51162306a36Sopenharmony_ci	}
51262306a36Sopenharmony_ci}
51362306a36Sopenharmony_ci#else
51462306a36Sopenharmony_ci#define DBGDATA(info, buf, size, label)
51562306a36Sopenharmony_ci#endif
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci#ifdef DBGTBUF
51862306a36Sopenharmony_cistatic void dump_tbufs(struct slgt_info *info)
51962306a36Sopenharmony_ci{
52062306a36Sopenharmony_ci	int i;
52162306a36Sopenharmony_ci	printk("tbuf_current=%d\n", info->tbuf_current);
52262306a36Sopenharmony_ci	for (i=0 ; i < info->tbuf_count ; i++) {
52362306a36Sopenharmony_ci		printk("%d: count=%04X status=%04X\n",
52462306a36Sopenharmony_ci			i, le16_to_cpu(info->tbufs[i].count), le16_to_cpu(info->tbufs[i].status));
52562306a36Sopenharmony_ci	}
52662306a36Sopenharmony_ci}
52762306a36Sopenharmony_ci#else
52862306a36Sopenharmony_ci#define DBGTBUF(info)
52962306a36Sopenharmony_ci#endif
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci#ifdef DBGRBUF
53262306a36Sopenharmony_cistatic void dump_rbufs(struct slgt_info *info)
53362306a36Sopenharmony_ci{
53462306a36Sopenharmony_ci	int i;
53562306a36Sopenharmony_ci	printk("rbuf_current=%d\n", info->rbuf_current);
53662306a36Sopenharmony_ci	for (i=0 ; i < info->rbuf_count ; i++) {
53762306a36Sopenharmony_ci		printk("%d: count=%04X status=%04X\n",
53862306a36Sopenharmony_ci			i, le16_to_cpu(info->rbufs[i].count), le16_to_cpu(info->rbufs[i].status));
53962306a36Sopenharmony_ci	}
54062306a36Sopenharmony_ci}
54162306a36Sopenharmony_ci#else
54262306a36Sopenharmony_ci#define DBGRBUF(info)
54362306a36Sopenharmony_ci#endif
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_cistatic inline int sanity_check(struct slgt_info *info, char *devname, const char *name)
54662306a36Sopenharmony_ci{
54762306a36Sopenharmony_ci#ifdef SANITY_CHECK
54862306a36Sopenharmony_ci	if (!info) {
54962306a36Sopenharmony_ci		printk("null struct slgt_info for (%s) in %s\n", devname, name);
55062306a36Sopenharmony_ci		return 1;
55162306a36Sopenharmony_ci	}
55262306a36Sopenharmony_ci#else
55362306a36Sopenharmony_ci	if (!info)
55462306a36Sopenharmony_ci		return 1;
55562306a36Sopenharmony_ci#endif
55662306a36Sopenharmony_ci	return 0;
55762306a36Sopenharmony_ci}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci/*
56062306a36Sopenharmony_ci * line discipline callback wrappers
56162306a36Sopenharmony_ci *
56262306a36Sopenharmony_ci * The wrappers maintain line discipline references
56362306a36Sopenharmony_ci * while calling into the line discipline.
56462306a36Sopenharmony_ci *
56562306a36Sopenharmony_ci * ldisc_receive_buf  - pass receive data to line discipline
56662306a36Sopenharmony_ci */
56762306a36Sopenharmony_cistatic void ldisc_receive_buf(struct tty_struct *tty,
56862306a36Sopenharmony_ci			      const __u8 *data, char *flags, int count)
56962306a36Sopenharmony_ci{
57062306a36Sopenharmony_ci	struct tty_ldisc *ld;
57162306a36Sopenharmony_ci	if (!tty)
57262306a36Sopenharmony_ci		return;
57362306a36Sopenharmony_ci	ld = tty_ldisc_ref(tty);
57462306a36Sopenharmony_ci	if (ld) {
57562306a36Sopenharmony_ci		if (ld->ops->receive_buf)
57662306a36Sopenharmony_ci			ld->ops->receive_buf(tty, data, flags, count);
57762306a36Sopenharmony_ci		tty_ldisc_deref(ld);
57862306a36Sopenharmony_ci	}
57962306a36Sopenharmony_ci}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci/* tty callbacks */
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_cistatic int open(struct tty_struct *tty, struct file *filp)
58462306a36Sopenharmony_ci{
58562306a36Sopenharmony_ci	struct slgt_info *info;
58662306a36Sopenharmony_ci	int retval, line;
58762306a36Sopenharmony_ci	unsigned long flags;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	line = tty->index;
59062306a36Sopenharmony_ci	if (line >= slgt_device_count) {
59162306a36Sopenharmony_ci		DBGERR(("%s: open with invalid line #%d.\n", driver_name, line));
59262306a36Sopenharmony_ci		return -ENODEV;
59362306a36Sopenharmony_ci	}
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	info = slgt_device_list;
59662306a36Sopenharmony_ci	while(info && info->line != line)
59762306a36Sopenharmony_ci		info = info->next_device;
59862306a36Sopenharmony_ci	if (sanity_check(info, tty->name, "open"))
59962306a36Sopenharmony_ci		return -ENODEV;
60062306a36Sopenharmony_ci	if (info->init_error) {
60162306a36Sopenharmony_ci		DBGERR(("%s init error=%d\n", info->device_name, info->init_error));
60262306a36Sopenharmony_ci		return -ENODEV;
60362306a36Sopenharmony_ci	}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	tty->driver_data = info;
60662306a36Sopenharmony_ci	info->port.tty = tty;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	DBGINFO(("%s open, old ref count = %d\n", info->device_name, info->port.count));
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	mutex_lock(&info->port.mutex);
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	spin_lock_irqsave(&info->netlock, flags);
61362306a36Sopenharmony_ci	if (info->netcount) {
61462306a36Sopenharmony_ci		retval = -EBUSY;
61562306a36Sopenharmony_ci		spin_unlock_irqrestore(&info->netlock, flags);
61662306a36Sopenharmony_ci		mutex_unlock(&info->port.mutex);
61762306a36Sopenharmony_ci		goto cleanup;
61862306a36Sopenharmony_ci	}
61962306a36Sopenharmony_ci	info->port.count++;
62062306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->netlock, flags);
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	if (info->port.count == 1) {
62362306a36Sopenharmony_ci		/* 1st open on this device, init hardware */
62462306a36Sopenharmony_ci		retval = startup(info);
62562306a36Sopenharmony_ci		if (retval < 0) {
62662306a36Sopenharmony_ci			mutex_unlock(&info->port.mutex);
62762306a36Sopenharmony_ci			goto cleanup;
62862306a36Sopenharmony_ci		}
62962306a36Sopenharmony_ci	}
63062306a36Sopenharmony_ci	mutex_unlock(&info->port.mutex);
63162306a36Sopenharmony_ci	retval = block_til_ready(tty, filp, info);
63262306a36Sopenharmony_ci	if (retval) {
63362306a36Sopenharmony_ci		DBGINFO(("%s block_til_ready rc=%d\n", info->device_name, retval));
63462306a36Sopenharmony_ci		goto cleanup;
63562306a36Sopenharmony_ci	}
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	retval = 0;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_cicleanup:
64062306a36Sopenharmony_ci	if (retval) {
64162306a36Sopenharmony_ci		if (tty->count == 1)
64262306a36Sopenharmony_ci			info->port.tty = NULL; /* tty layer will release tty struct */
64362306a36Sopenharmony_ci		if(info->port.count)
64462306a36Sopenharmony_ci			info->port.count--;
64562306a36Sopenharmony_ci	}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	DBGINFO(("%s open rc=%d\n", info->device_name, retval));
64862306a36Sopenharmony_ci	return retval;
64962306a36Sopenharmony_ci}
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_cistatic void close(struct tty_struct *tty, struct file *filp)
65262306a36Sopenharmony_ci{
65362306a36Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	if (sanity_check(info, tty->name, "close"))
65662306a36Sopenharmony_ci		return;
65762306a36Sopenharmony_ci	DBGINFO(("%s close entry, count=%d\n", info->device_name, info->port.count));
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	if (tty_port_close_start(&info->port, tty, filp) == 0)
66062306a36Sopenharmony_ci		goto cleanup;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	mutex_lock(&info->port.mutex);
66362306a36Sopenharmony_ci	if (tty_port_initialized(&info->port))
66462306a36Sopenharmony_ci 		wait_until_sent(tty, info->timeout);
66562306a36Sopenharmony_ci	flush_buffer(tty);
66662306a36Sopenharmony_ci	tty_ldisc_flush(tty);
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	shutdown(info);
66962306a36Sopenharmony_ci	mutex_unlock(&info->port.mutex);
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	tty_port_close_end(&info->port, tty);
67262306a36Sopenharmony_ci	info->port.tty = NULL;
67362306a36Sopenharmony_cicleanup:
67462306a36Sopenharmony_ci	DBGINFO(("%s close exit, count=%d\n", tty->driver->name, info->port.count));
67562306a36Sopenharmony_ci}
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_cistatic void hangup(struct tty_struct *tty)
67862306a36Sopenharmony_ci{
67962306a36Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
68062306a36Sopenharmony_ci	unsigned long flags;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	if (sanity_check(info, tty->name, "hangup"))
68362306a36Sopenharmony_ci		return;
68462306a36Sopenharmony_ci	DBGINFO(("%s hangup\n", info->device_name));
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	flush_buffer(tty);
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	mutex_lock(&info->port.mutex);
68962306a36Sopenharmony_ci	shutdown(info);
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	spin_lock_irqsave(&info->port.lock, flags);
69262306a36Sopenharmony_ci	info->port.count = 0;
69362306a36Sopenharmony_ci	info->port.tty = NULL;
69462306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->port.lock, flags);
69562306a36Sopenharmony_ci	tty_port_set_active(&info->port, false);
69662306a36Sopenharmony_ci	mutex_unlock(&info->port.mutex);
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	wake_up_interruptible(&info->port.open_wait);
69962306a36Sopenharmony_ci}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_cistatic void set_termios(struct tty_struct *tty,
70262306a36Sopenharmony_ci			const struct ktermios *old_termios)
70362306a36Sopenharmony_ci{
70462306a36Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
70562306a36Sopenharmony_ci	unsigned long flags;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	DBGINFO(("%s set_termios\n", tty->driver->name));
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	change_params(info);
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	/* Handle transition to B0 status */
71262306a36Sopenharmony_ci	if ((old_termios->c_cflag & CBAUD) && !C_BAUD(tty)) {
71362306a36Sopenharmony_ci		info->signals &= ~(SerialSignal_RTS | SerialSignal_DTR);
71462306a36Sopenharmony_ci		spin_lock_irqsave(&info->lock,flags);
71562306a36Sopenharmony_ci		set_gtsignals(info);
71662306a36Sopenharmony_ci		spin_unlock_irqrestore(&info->lock,flags);
71762306a36Sopenharmony_ci	}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	/* Handle transition away from B0 status */
72062306a36Sopenharmony_ci	if (!(old_termios->c_cflag & CBAUD) && C_BAUD(tty)) {
72162306a36Sopenharmony_ci		info->signals |= SerialSignal_DTR;
72262306a36Sopenharmony_ci		if (!C_CRTSCTS(tty) || !tty_throttled(tty))
72362306a36Sopenharmony_ci			info->signals |= SerialSignal_RTS;
72462306a36Sopenharmony_ci		spin_lock_irqsave(&info->lock,flags);
72562306a36Sopenharmony_ci	 	set_gtsignals(info);
72662306a36Sopenharmony_ci		spin_unlock_irqrestore(&info->lock,flags);
72762306a36Sopenharmony_ci	}
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	/* Handle turning off CRTSCTS */
73062306a36Sopenharmony_ci	if ((old_termios->c_cflag & CRTSCTS) && !C_CRTSCTS(tty)) {
73162306a36Sopenharmony_ci		tty->hw_stopped = false;
73262306a36Sopenharmony_ci		tx_release(tty);
73362306a36Sopenharmony_ci	}
73462306a36Sopenharmony_ci}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_cistatic void update_tx_timer(struct slgt_info *info)
73762306a36Sopenharmony_ci{
73862306a36Sopenharmony_ci	/*
73962306a36Sopenharmony_ci	 * use worst case speed of 1200bps to calculate transmit timeout
74062306a36Sopenharmony_ci	 * based on data in buffers (tbuf_bytes) and FIFO (128 bytes)
74162306a36Sopenharmony_ci	 */
74262306a36Sopenharmony_ci	if (info->params.mode == MGSL_MODE_HDLC) {
74362306a36Sopenharmony_ci		int timeout  = (tbuf_bytes(info) * 7) + 1000;
74462306a36Sopenharmony_ci		mod_timer(&info->tx_timer, jiffies + msecs_to_jiffies(timeout));
74562306a36Sopenharmony_ci	}
74662306a36Sopenharmony_ci}
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_cistatic ssize_t write(struct tty_struct *tty, const u8 *buf, size_t count)
74962306a36Sopenharmony_ci{
75062306a36Sopenharmony_ci	int ret = 0;
75162306a36Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
75262306a36Sopenharmony_ci	unsigned long flags;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	if (sanity_check(info, tty->name, "write"))
75562306a36Sopenharmony_ci		return -EIO;
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	DBGINFO(("%s write count=%zu\n", info->device_name, count));
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	if (!info->tx_buf || (count > info->max_frame_size))
76062306a36Sopenharmony_ci		return -EIO;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	if (!count || tty->flow.stopped || tty->hw_stopped)
76362306a36Sopenharmony_ci		return 0;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock, flags);
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	if (info->tx_count) {
76862306a36Sopenharmony_ci		/* send accumulated data from send_char() */
76962306a36Sopenharmony_ci		if (!tx_load(info, info->tx_buf, info->tx_count))
77062306a36Sopenharmony_ci			goto cleanup;
77162306a36Sopenharmony_ci		info->tx_count = 0;
77262306a36Sopenharmony_ci	}
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	if (tx_load(info, buf, count))
77562306a36Sopenharmony_ci		ret = count;
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_cicleanup:
77862306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->lock, flags);
77962306a36Sopenharmony_ci	DBGINFO(("%s write rc=%d\n", info->device_name, ret));
78062306a36Sopenharmony_ci	return ret;
78162306a36Sopenharmony_ci}
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_cistatic int put_char(struct tty_struct *tty, u8 ch)
78462306a36Sopenharmony_ci{
78562306a36Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
78662306a36Sopenharmony_ci	unsigned long flags;
78762306a36Sopenharmony_ci	int ret = 0;
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	if (sanity_check(info, tty->name, "put_char"))
79062306a36Sopenharmony_ci		return 0;
79162306a36Sopenharmony_ci	DBGINFO(("%s put_char(%u)\n", info->device_name, ch));
79262306a36Sopenharmony_ci	if (!info->tx_buf)
79362306a36Sopenharmony_ci		return 0;
79462306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
79562306a36Sopenharmony_ci	if (info->tx_count < info->max_frame_size) {
79662306a36Sopenharmony_ci		info->tx_buf[info->tx_count++] = ch;
79762306a36Sopenharmony_ci		ret = 1;
79862306a36Sopenharmony_ci	}
79962306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
80062306a36Sopenharmony_ci	return ret;
80162306a36Sopenharmony_ci}
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_cistatic void send_xchar(struct tty_struct *tty, char ch)
80462306a36Sopenharmony_ci{
80562306a36Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
80662306a36Sopenharmony_ci	unsigned long flags;
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	if (sanity_check(info, tty->name, "send_xchar"))
80962306a36Sopenharmony_ci		return;
81062306a36Sopenharmony_ci	DBGINFO(("%s send_xchar(%d)\n", info->device_name, ch));
81162306a36Sopenharmony_ci	info->x_char = ch;
81262306a36Sopenharmony_ci	if (ch) {
81362306a36Sopenharmony_ci		spin_lock_irqsave(&info->lock,flags);
81462306a36Sopenharmony_ci		if (!info->tx_enabled)
81562306a36Sopenharmony_ci		 	tx_start(info);
81662306a36Sopenharmony_ci		spin_unlock_irqrestore(&info->lock,flags);
81762306a36Sopenharmony_ci	}
81862306a36Sopenharmony_ci}
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_cistatic void wait_until_sent(struct tty_struct *tty, int timeout)
82162306a36Sopenharmony_ci{
82262306a36Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
82362306a36Sopenharmony_ci	unsigned long orig_jiffies, char_time;
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	if (!info )
82662306a36Sopenharmony_ci		return;
82762306a36Sopenharmony_ci	if (sanity_check(info, tty->name, "wait_until_sent"))
82862306a36Sopenharmony_ci		return;
82962306a36Sopenharmony_ci	DBGINFO(("%s wait_until_sent entry\n", info->device_name));
83062306a36Sopenharmony_ci	if (!tty_port_initialized(&info->port))
83162306a36Sopenharmony_ci		goto exit;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	orig_jiffies = jiffies;
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	/* Set check interval to 1/5 of estimated time to
83662306a36Sopenharmony_ci	 * send a character, and make it at least 1. The check
83762306a36Sopenharmony_ci	 * interval should also be less than the timeout.
83862306a36Sopenharmony_ci	 * Note: use tight timings here to satisfy the NIST-PCTS.
83962306a36Sopenharmony_ci	 */
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	if (info->params.data_rate) {
84262306a36Sopenharmony_ci	       	char_time = info->timeout/(32 * 5);
84362306a36Sopenharmony_ci		if (!char_time)
84462306a36Sopenharmony_ci			char_time++;
84562306a36Sopenharmony_ci	} else
84662306a36Sopenharmony_ci		char_time = 1;
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	if (timeout)
84962306a36Sopenharmony_ci		char_time = min_t(unsigned long, char_time, timeout);
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	while (info->tx_active) {
85262306a36Sopenharmony_ci		msleep_interruptible(jiffies_to_msecs(char_time));
85362306a36Sopenharmony_ci		if (signal_pending(current))
85462306a36Sopenharmony_ci			break;
85562306a36Sopenharmony_ci		if (timeout && time_after(jiffies, orig_jiffies + timeout))
85662306a36Sopenharmony_ci			break;
85762306a36Sopenharmony_ci	}
85862306a36Sopenharmony_ciexit:
85962306a36Sopenharmony_ci	DBGINFO(("%s wait_until_sent exit\n", info->device_name));
86062306a36Sopenharmony_ci}
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_cistatic unsigned int write_room(struct tty_struct *tty)
86362306a36Sopenharmony_ci{
86462306a36Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
86562306a36Sopenharmony_ci	unsigned int ret;
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	if (sanity_check(info, tty->name, "write_room"))
86862306a36Sopenharmony_ci		return 0;
86962306a36Sopenharmony_ci	ret = (info->tx_active) ? 0 : HDLC_MAX_FRAME_SIZE;
87062306a36Sopenharmony_ci	DBGINFO(("%s write_room=%u\n", info->device_name, ret));
87162306a36Sopenharmony_ci	return ret;
87262306a36Sopenharmony_ci}
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_cistatic void flush_chars(struct tty_struct *tty)
87562306a36Sopenharmony_ci{
87662306a36Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
87762306a36Sopenharmony_ci	unsigned long flags;
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	if (sanity_check(info, tty->name, "flush_chars"))
88062306a36Sopenharmony_ci		return;
88162306a36Sopenharmony_ci	DBGINFO(("%s flush_chars entry tx_count=%d\n", info->device_name, info->tx_count));
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	if (info->tx_count <= 0 || tty->flow.stopped ||
88462306a36Sopenharmony_ci	    tty->hw_stopped || !info->tx_buf)
88562306a36Sopenharmony_ci		return;
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	DBGINFO(("%s flush_chars start transmit\n", info->device_name));
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
89062306a36Sopenharmony_ci	if (info->tx_count && tx_load(info, info->tx_buf, info->tx_count))
89162306a36Sopenharmony_ci		info->tx_count = 0;
89262306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
89362306a36Sopenharmony_ci}
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_cistatic void flush_buffer(struct tty_struct *tty)
89662306a36Sopenharmony_ci{
89762306a36Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
89862306a36Sopenharmony_ci	unsigned long flags;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	if (sanity_check(info, tty->name, "flush_buffer"))
90162306a36Sopenharmony_ci		return;
90262306a36Sopenharmony_ci	DBGINFO(("%s flush_buffer\n", info->device_name));
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock, flags);
90562306a36Sopenharmony_ci	info->tx_count = 0;
90662306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->lock, flags);
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	tty_wakeup(tty);
90962306a36Sopenharmony_ci}
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci/*
91262306a36Sopenharmony_ci * throttle (stop) transmitter
91362306a36Sopenharmony_ci */
91462306a36Sopenharmony_cistatic void tx_hold(struct tty_struct *tty)
91562306a36Sopenharmony_ci{
91662306a36Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
91762306a36Sopenharmony_ci	unsigned long flags;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	if (sanity_check(info, tty->name, "tx_hold"))
92062306a36Sopenharmony_ci		return;
92162306a36Sopenharmony_ci	DBGINFO(("%s tx_hold\n", info->device_name));
92262306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
92362306a36Sopenharmony_ci	if (info->tx_enabled && info->params.mode == MGSL_MODE_ASYNC)
92462306a36Sopenharmony_ci	 	tx_stop(info);
92562306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
92662306a36Sopenharmony_ci}
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci/*
92962306a36Sopenharmony_ci * release (start) transmitter
93062306a36Sopenharmony_ci */
93162306a36Sopenharmony_cistatic void tx_release(struct tty_struct *tty)
93262306a36Sopenharmony_ci{
93362306a36Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
93462306a36Sopenharmony_ci	unsigned long flags;
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	if (sanity_check(info, tty->name, "tx_release"))
93762306a36Sopenharmony_ci		return;
93862306a36Sopenharmony_ci	DBGINFO(("%s tx_release\n", info->device_name));
93962306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock, flags);
94062306a36Sopenharmony_ci	if (info->tx_count && tx_load(info, info->tx_buf, info->tx_count))
94162306a36Sopenharmony_ci		info->tx_count = 0;
94262306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->lock, flags);
94362306a36Sopenharmony_ci}
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci/*
94662306a36Sopenharmony_ci * Service an IOCTL request
94762306a36Sopenharmony_ci *
94862306a36Sopenharmony_ci * Arguments
94962306a36Sopenharmony_ci *
95062306a36Sopenharmony_ci * 	tty	pointer to tty instance data
95162306a36Sopenharmony_ci * 	cmd	IOCTL command code
95262306a36Sopenharmony_ci * 	arg	command argument/context
95362306a36Sopenharmony_ci *
95462306a36Sopenharmony_ci * Return 0 if success, otherwise error code
95562306a36Sopenharmony_ci */
95662306a36Sopenharmony_cistatic int ioctl(struct tty_struct *tty,
95762306a36Sopenharmony_ci		 unsigned int cmd, unsigned long arg)
95862306a36Sopenharmony_ci{
95962306a36Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
96062306a36Sopenharmony_ci	void __user *argp = (void __user *)arg;
96162306a36Sopenharmony_ci	int ret;
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	if (sanity_check(info, tty->name, "ioctl"))
96462306a36Sopenharmony_ci		return -ENODEV;
96562306a36Sopenharmony_ci	DBGINFO(("%s ioctl() cmd=%08X\n", info->device_name, cmd));
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	if (cmd != TIOCMIWAIT) {
96862306a36Sopenharmony_ci		if (tty_io_error(tty))
96962306a36Sopenharmony_ci		    return -EIO;
97062306a36Sopenharmony_ci	}
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	switch (cmd) {
97362306a36Sopenharmony_ci	case MGSL_IOCWAITEVENT:
97462306a36Sopenharmony_ci		return wait_mgsl_event(info, argp);
97562306a36Sopenharmony_ci	case TIOCMIWAIT:
97662306a36Sopenharmony_ci		return modem_input_wait(info,(int)arg);
97762306a36Sopenharmony_ci	case MGSL_IOCSGPIO:
97862306a36Sopenharmony_ci		return set_gpio(info, argp);
97962306a36Sopenharmony_ci	case MGSL_IOCGGPIO:
98062306a36Sopenharmony_ci		return get_gpio(info, argp);
98162306a36Sopenharmony_ci	case MGSL_IOCWAITGPIO:
98262306a36Sopenharmony_ci		return wait_gpio(info, argp);
98362306a36Sopenharmony_ci	case MGSL_IOCGXSYNC:
98462306a36Sopenharmony_ci		return get_xsync(info, argp);
98562306a36Sopenharmony_ci	case MGSL_IOCSXSYNC:
98662306a36Sopenharmony_ci		return set_xsync(info, (int)arg);
98762306a36Sopenharmony_ci	case MGSL_IOCGXCTRL:
98862306a36Sopenharmony_ci		return get_xctrl(info, argp);
98962306a36Sopenharmony_ci	case MGSL_IOCSXCTRL:
99062306a36Sopenharmony_ci		return set_xctrl(info, (int)arg);
99162306a36Sopenharmony_ci	}
99262306a36Sopenharmony_ci	mutex_lock(&info->port.mutex);
99362306a36Sopenharmony_ci	switch (cmd) {
99462306a36Sopenharmony_ci	case MGSL_IOCGPARAMS:
99562306a36Sopenharmony_ci		ret = get_params(info, argp);
99662306a36Sopenharmony_ci		break;
99762306a36Sopenharmony_ci	case MGSL_IOCSPARAMS:
99862306a36Sopenharmony_ci		ret = set_params(info, argp);
99962306a36Sopenharmony_ci		break;
100062306a36Sopenharmony_ci	case MGSL_IOCGTXIDLE:
100162306a36Sopenharmony_ci		ret = get_txidle(info, argp);
100262306a36Sopenharmony_ci		break;
100362306a36Sopenharmony_ci	case MGSL_IOCSTXIDLE:
100462306a36Sopenharmony_ci		ret = set_txidle(info, (int)arg);
100562306a36Sopenharmony_ci		break;
100662306a36Sopenharmony_ci	case MGSL_IOCTXENABLE:
100762306a36Sopenharmony_ci		ret = tx_enable(info, (int)arg);
100862306a36Sopenharmony_ci		break;
100962306a36Sopenharmony_ci	case MGSL_IOCRXENABLE:
101062306a36Sopenharmony_ci		ret = rx_enable(info, (int)arg);
101162306a36Sopenharmony_ci		break;
101262306a36Sopenharmony_ci	case MGSL_IOCTXABORT:
101362306a36Sopenharmony_ci		ret = tx_abort(info);
101462306a36Sopenharmony_ci		break;
101562306a36Sopenharmony_ci	case MGSL_IOCGSTATS:
101662306a36Sopenharmony_ci		ret = get_stats(info, argp);
101762306a36Sopenharmony_ci		break;
101862306a36Sopenharmony_ci	case MGSL_IOCGIF:
101962306a36Sopenharmony_ci		ret = get_interface(info, argp);
102062306a36Sopenharmony_ci		break;
102162306a36Sopenharmony_ci	case MGSL_IOCSIF:
102262306a36Sopenharmony_ci		ret = set_interface(info,(int)arg);
102362306a36Sopenharmony_ci		break;
102462306a36Sopenharmony_ci	default:
102562306a36Sopenharmony_ci		ret = -ENOIOCTLCMD;
102662306a36Sopenharmony_ci	}
102762306a36Sopenharmony_ci	mutex_unlock(&info->port.mutex);
102862306a36Sopenharmony_ci	return ret;
102962306a36Sopenharmony_ci}
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_cistatic int get_icount(struct tty_struct *tty,
103262306a36Sopenharmony_ci				struct serial_icounter_struct *icount)
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci{
103562306a36Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
103662306a36Sopenharmony_ci	struct mgsl_icount cnow;	/* kernel counter temps */
103762306a36Sopenharmony_ci	unsigned long flags;
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
104062306a36Sopenharmony_ci	cnow = info->icount;
104162306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	icount->cts = cnow.cts;
104462306a36Sopenharmony_ci	icount->dsr = cnow.dsr;
104562306a36Sopenharmony_ci	icount->rng = cnow.rng;
104662306a36Sopenharmony_ci	icount->dcd = cnow.dcd;
104762306a36Sopenharmony_ci	icount->rx = cnow.rx;
104862306a36Sopenharmony_ci	icount->tx = cnow.tx;
104962306a36Sopenharmony_ci	icount->frame = cnow.frame;
105062306a36Sopenharmony_ci	icount->overrun = cnow.overrun;
105162306a36Sopenharmony_ci	icount->parity = cnow.parity;
105262306a36Sopenharmony_ci	icount->brk = cnow.brk;
105362306a36Sopenharmony_ci	icount->buf_overrun = cnow.buf_overrun;
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	return 0;
105662306a36Sopenharmony_ci}
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci/*
105962306a36Sopenharmony_ci * support for 32 bit ioctl calls on 64 bit systems
106062306a36Sopenharmony_ci */
106162306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
106262306a36Sopenharmony_cistatic long get_params32(struct slgt_info *info, struct MGSL_PARAMS32 __user *user_params)
106362306a36Sopenharmony_ci{
106462306a36Sopenharmony_ci	struct MGSL_PARAMS32 tmp_params;
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	DBGINFO(("%s get_params32\n", info->device_name));
106762306a36Sopenharmony_ci	memset(&tmp_params, 0, sizeof(tmp_params));
106862306a36Sopenharmony_ci	tmp_params.mode            = (compat_ulong_t)info->params.mode;
106962306a36Sopenharmony_ci	tmp_params.loopback        = info->params.loopback;
107062306a36Sopenharmony_ci	tmp_params.flags           = info->params.flags;
107162306a36Sopenharmony_ci	tmp_params.encoding        = info->params.encoding;
107262306a36Sopenharmony_ci	tmp_params.clock_speed     = (compat_ulong_t)info->params.clock_speed;
107362306a36Sopenharmony_ci	tmp_params.addr_filter     = info->params.addr_filter;
107462306a36Sopenharmony_ci	tmp_params.crc_type        = info->params.crc_type;
107562306a36Sopenharmony_ci	tmp_params.preamble_length = info->params.preamble_length;
107662306a36Sopenharmony_ci	tmp_params.preamble        = info->params.preamble;
107762306a36Sopenharmony_ci	tmp_params.data_rate       = (compat_ulong_t)info->params.data_rate;
107862306a36Sopenharmony_ci	tmp_params.data_bits       = info->params.data_bits;
107962306a36Sopenharmony_ci	tmp_params.stop_bits       = info->params.stop_bits;
108062306a36Sopenharmony_ci	tmp_params.parity          = info->params.parity;
108162306a36Sopenharmony_ci	if (copy_to_user(user_params, &tmp_params, sizeof(struct MGSL_PARAMS32)))
108262306a36Sopenharmony_ci		return -EFAULT;
108362306a36Sopenharmony_ci	return 0;
108462306a36Sopenharmony_ci}
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_cistatic long set_params32(struct slgt_info *info, struct MGSL_PARAMS32 __user *new_params)
108762306a36Sopenharmony_ci{
108862306a36Sopenharmony_ci	struct MGSL_PARAMS32 tmp_params;
108962306a36Sopenharmony_ci	unsigned long flags;
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	DBGINFO(("%s set_params32\n", info->device_name));
109262306a36Sopenharmony_ci	if (copy_from_user(&tmp_params, new_params, sizeof(struct MGSL_PARAMS32)))
109362306a36Sopenharmony_ci		return -EFAULT;
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock, flags);
109662306a36Sopenharmony_ci	if (tmp_params.mode == MGSL_MODE_BASE_CLOCK) {
109762306a36Sopenharmony_ci		info->base_clock = tmp_params.clock_speed;
109862306a36Sopenharmony_ci	} else {
109962306a36Sopenharmony_ci		info->params.mode            = tmp_params.mode;
110062306a36Sopenharmony_ci		info->params.loopback        = tmp_params.loopback;
110162306a36Sopenharmony_ci		info->params.flags           = tmp_params.flags;
110262306a36Sopenharmony_ci		info->params.encoding        = tmp_params.encoding;
110362306a36Sopenharmony_ci		info->params.clock_speed     = tmp_params.clock_speed;
110462306a36Sopenharmony_ci		info->params.addr_filter     = tmp_params.addr_filter;
110562306a36Sopenharmony_ci		info->params.crc_type        = tmp_params.crc_type;
110662306a36Sopenharmony_ci		info->params.preamble_length = tmp_params.preamble_length;
110762306a36Sopenharmony_ci		info->params.preamble        = tmp_params.preamble;
110862306a36Sopenharmony_ci		info->params.data_rate       = tmp_params.data_rate;
110962306a36Sopenharmony_ci		info->params.data_bits       = tmp_params.data_bits;
111062306a36Sopenharmony_ci		info->params.stop_bits       = tmp_params.stop_bits;
111162306a36Sopenharmony_ci		info->params.parity          = tmp_params.parity;
111262306a36Sopenharmony_ci	}
111362306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->lock, flags);
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci	program_hw(info);
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci	return 0;
111862306a36Sopenharmony_ci}
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_cistatic long slgt_compat_ioctl(struct tty_struct *tty,
112162306a36Sopenharmony_ci			 unsigned int cmd, unsigned long arg)
112262306a36Sopenharmony_ci{
112362306a36Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
112462306a36Sopenharmony_ci	int rc;
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	if (sanity_check(info, tty->name, "compat_ioctl"))
112762306a36Sopenharmony_ci		return -ENODEV;
112862306a36Sopenharmony_ci	DBGINFO(("%s compat_ioctl() cmd=%08X\n", info->device_name, cmd));
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	switch (cmd) {
113162306a36Sopenharmony_ci	case MGSL_IOCSPARAMS32:
113262306a36Sopenharmony_ci		rc = set_params32(info, compat_ptr(arg));
113362306a36Sopenharmony_ci		break;
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	case MGSL_IOCGPARAMS32:
113662306a36Sopenharmony_ci		rc = get_params32(info, compat_ptr(arg));
113762306a36Sopenharmony_ci		break;
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	case MGSL_IOCGPARAMS:
114062306a36Sopenharmony_ci	case MGSL_IOCSPARAMS:
114162306a36Sopenharmony_ci	case MGSL_IOCGTXIDLE:
114262306a36Sopenharmony_ci	case MGSL_IOCGSTATS:
114362306a36Sopenharmony_ci	case MGSL_IOCWAITEVENT:
114462306a36Sopenharmony_ci	case MGSL_IOCGIF:
114562306a36Sopenharmony_ci	case MGSL_IOCSGPIO:
114662306a36Sopenharmony_ci	case MGSL_IOCGGPIO:
114762306a36Sopenharmony_ci	case MGSL_IOCWAITGPIO:
114862306a36Sopenharmony_ci	case MGSL_IOCGXSYNC:
114962306a36Sopenharmony_ci	case MGSL_IOCGXCTRL:
115062306a36Sopenharmony_ci		rc = ioctl(tty, cmd, (unsigned long)compat_ptr(arg));
115162306a36Sopenharmony_ci		break;
115262306a36Sopenharmony_ci	default:
115362306a36Sopenharmony_ci		rc = ioctl(tty, cmd, arg);
115462306a36Sopenharmony_ci	}
115562306a36Sopenharmony_ci	DBGINFO(("%s compat_ioctl() cmd=%08X rc=%d\n", info->device_name, cmd, rc));
115662306a36Sopenharmony_ci	return rc;
115762306a36Sopenharmony_ci}
115862306a36Sopenharmony_ci#else
115962306a36Sopenharmony_ci#define slgt_compat_ioctl NULL
116062306a36Sopenharmony_ci#endif /* ifdef CONFIG_COMPAT */
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci/*
116362306a36Sopenharmony_ci * proc fs support
116462306a36Sopenharmony_ci */
116562306a36Sopenharmony_cistatic inline void line_info(struct seq_file *m, struct slgt_info *info)
116662306a36Sopenharmony_ci{
116762306a36Sopenharmony_ci	char stat_buf[30];
116862306a36Sopenharmony_ci	unsigned long flags;
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci	seq_printf(m, "%s: IO=%08X IRQ=%d MaxFrameSize=%u\n",
117162306a36Sopenharmony_ci		      info->device_name, info->phys_reg_addr,
117262306a36Sopenharmony_ci		      info->irq_level, info->max_frame_size);
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	/* output current serial signal states */
117562306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
117662306a36Sopenharmony_ci	get_gtsignals(info);
117762306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	stat_buf[0] = 0;
118062306a36Sopenharmony_ci	stat_buf[1] = 0;
118162306a36Sopenharmony_ci	if (info->signals & SerialSignal_RTS)
118262306a36Sopenharmony_ci		strcat(stat_buf, "|RTS");
118362306a36Sopenharmony_ci	if (info->signals & SerialSignal_CTS)
118462306a36Sopenharmony_ci		strcat(stat_buf, "|CTS");
118562306a36Sopenharmony_ci	if (info->signals & SerialSignal_DTR)
118662306a36Sopenharmony_ci		strcat(stat_buf, "|DTR");
118762306a36Sopenharmony_ci	if (info->signals & SerialSignal_DSR)
118862306a36Sopenharmony_ci		strcat(stat_buf, "|DSR");
118962306a36Sopenharmony_ci	if (info->signals & SerialSignal_DCD)
119062306a36Sopenharmony_ci		strcat(stat_buf, "|CD");
119162306a36Sopenharmony_ci	if (info->signals & SerialSignal_RI)
119262306a36Sopenharmony_ci		strcat(stat_buf, "|RI");
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci	if (info->params.mode != MGSL_MODE_ASYNC) {
119562306a36Sopenharmony_ci		seq_printf(m, "\tHDLC txok:%d rxok:%d",
119662306a36Sopenharmony_ci			       info->icount.txok, info->icount.rxok);
119762306a36Sopenharmony_ci		if (info->icount.txunder)
119862306a36Sopenharmony_ci			seq_printf(m, " txunder:%d", info->icount.txunder);
119962306a36Sopenharmony_ci		if (info->icount.txabort)
120062306a36Sopenharmony_ci			seq_printf(m, " txabort:%d", info->icount.txabort);
120162306a36Sopenharmony_ci		if (info->icount.rxshort)
120262306a36Sopenharmony_ci			seq_printf(m, " rxshort:%d", info->icount.rxshort);
120362306a36Sopenharmony_ci		if (info->icount.rxlong)
120462306a36Sopenharmony_ci			seq_printf(m, " rxlong:%d", info->icount.rxlong);
120562306a36Sopenharmony_ci		if (info->icount.rxover)
120662306a36Sopenharmony_ci			seq_printf(m, " rxover:%d", info->icount.rxover);
120762306a36Sopenharmony_ci		if (info->icount.rxcrc)
120862306a36Sopenharmony_ci			seq_printf(m, " rxcrc:%d", info->icount.rxcrc);
120962306a36Sopenharmony_ci	} else {
121062306a36Sopenharmony_ci		seq_printf(m, "\tASYNC tx:%d rx:%d",
121162306a36Sopenharmony_ci			       info->icount.tx, info->icount.rx);
121262306a36Sopenharmony_ci		if (info->icount.frame)
121362306a36Sopenharmony_ci			seq_printf(m, " fe:%d", info->icount.frame);
121462306a36Sopenharmony_ci		if (info->icount.parity)
121562306a36Sopenharmony_ci			seq_printf(m, " pe:%d", info->icount.parity);
121662306a36Sopenharmony_ci		if (info->icount.brk)
121762306a36Sopenharmony_ci			seq_printf(m, " brk:%d", info->icount.brk);
121862306a36Sopenharmony_ci		if (info->icount.overrun)
121962306a36Sopenharmony_ci			seq_printf(m, " oe:%d", info->icount.overrun);
122062306a36Sopenharmony_ci	}
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	/* Append serial signal status to end */
122362306a36Sopenharmony_ci	seq_printf(m, " %s\n", stat_buf+1);
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	seq_printf(m, "\ttxactive=%d bh_req=%d bh_run=%d pending_bh=%x\n",
122662306a36Sopenharmony_ci		       info->tx_active,info->bh_requested,info->bh_running,
122762306a36Sopenharmony_ci		       info->pending_bh);
122862306a36Sopenharmony_ci}
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci/* Called to print information about devices
123162306a36Sopenharmony_ci */
123262306a36Sopenharmony_cistatic int synclink_gt_proc_show(struct seq_file *m, void *v)
123362306a36Sopenharmony_ci{
123462306a36Sopenharmony_ci	struct slgt_info *info;
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	seq_puts(m, "synclink_gt driver\n");
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci	info = slgt_device_list;
123962306a36Sopenharmony_ci	while( info ) {
124062306a36Sopenharmony_ci		line_info(m, info);
124162306a36Sopenharmony_ci		info = info->next_device;
124262306a36Sopenharmony_ci	}
124362306a36Sopenharmony_ci	return 0;
124462306a36Sopenharmony_ci}
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci/*
124762306a36Sopenharmony_ci * return count of bytes in transmit buffer
124862306a36Sopenharmony_ci */
124962306a36Sopenharmony_cistatic unsigned int chars_in_buffer(struct tty_struct *tty)
125062306a36Sopenharmony_ci{
125162306a36Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
125262306a36Sopenharmony_ci	unsigned int count;
125362306a36Sopenharmony_ci	if (sanity_check(info, tty->name, "chars_in_buffer"))
125462306a36Sopenharmony_ci		return 0;
125562306a36Sopenharmony_ci	count = tbuf_bytes(info);
125662306a36Sopenharmony_ci	DBGINFO(("%s chars_in_buffer()=%u\n", info->device_name, count));
125762306a36Sopenharmony_ci	return count;
125862306a36Sopenharmony_ci}
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci/*
126162306a36Sopenharmony_ci * signal remote device to throttle send data (our receive data)
126262306a36Sopenharmony_ci */
126362306a36Sopenharmony_cistatic void throttle(struct tty_struct * tty)
126462306a36Sopenharmony_ci{
126562306a36Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
126662306a36Sopenharmony_ci	unsigned long flags;
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	if (sanity_check(info, tty->name, "throttle"))
126962306a36Sopenharmony_ci		return;
127062306a36Sopenharmony_ci	DBGINFO(("%s throttle\n", info->device_name));
127162306a36Sopenharmony_ci	if (I_IXOFF(tty))
127262306a36Sopenharmony_ci		send_xchar(tty, STOP_CHAR(tty));
127362306a36Sopenharmony_ci	if (C_CRTSCTS(tty)) {
127462306a36Sopenharmony_ci		spin_lock_irqsave(&info->lock,flags);
127562306a36Sopenharmony_ci		info->signals &= ~SerialSignal_RTS;
127662306a36Sopenharmony_ci		set_gtsignals(info);
127762306a36Sopenharmony_ci		spin_unlock_irqrestore(&info->lock,flags);
127862306a36Sopenharmony_ci	}
127962306a36Sopenharmony_ci}
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci/*
128262306a36Sopenharmony_ci * signal remote device to stop throttling send data (our receive data)
128362306a36Sopenharmony_ci */
128462306a36Sopenharmony_cistatic void unthrottle(struct tty_struct * tty)
128562306a36Sopenharmony_ci{
128662306a36Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
128762306a36Sopenharmony_ci	unsigned long flags;
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	if (sanity_check(info, tty->name, "unthrottle"))
129062306a36Sopenharmony_ci		return;
129162306a36Sopenharmony_ci	DBGINFO(("%s unthrottle\n", info->device_name));
129262306a36Sopenharmony_ci	if (I_IXOFF(tty)) {
129362306a36Sopenharmony_ci		if (info->x_char)
129462306a36Sopenharmony_ci			info->x_char = 0;
129562306a36Sopenharmony_ci		else
129662306a36Sopenharmony_ci			send_xchar(tty, START_CHAR(tty));
129762306a36Sopenharmony_ci	}
129862306a36Sopenharmony_ci	if (C_CRTSCTS(tty)) {
129962306a36Sopenharmony_ci		spin_lock_irqsave(&info->lock,flags);
130062306a36Sopenharmony_ci		info->signals |= SerialSignal_RTS;
130162306a36Sopenharmony_ci		set_gtsignals(info);
130262306a36Sopenharmony_ci		spin_unlock_irqrestore(&info->lock,flags);
130362306a36Sopenharmony_ci	}
130462306a36Sopenharmony_ci}
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci/*
130762306a36Sopenharmony_ci * set or clear transmit break condition
130862306a36Sopenharmony_ci * break_state	-1=set break condition, 0=clear
130962306a36Sopenharmony_ci */
131062306a36Sopenharmony_cistatic int set_break(struct tty_struct *tty, int break_state)
131162306a36Sopenharmony_ci{
131262306a36Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
131362306a36Sopenharmony_ci	unsigned short value;
131462306a36Sopenharmony_ci	unsigned long flags;
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci	if (sanity_check(info, tty->name, "set_break"))
131762306a36Sopenharmony_ci		return -EINVAL;
131862306a36Sopenharmony_ci	DBGINFO(("%s set_break(%d)\n", info->device_name, break_state));
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
132162306a36Sopenharmony_ci	value = rd_reg16(info, TCR);
132262306a36Sopenharmony_ci 	if (break_state == -1)
132362306a36Sopenharmony_ci		value |= BIT6;
132462306a36Sopenharmony_ci	else
132562306a36Sopenharmony_ci		value &= ~BIT6;
132662306a36Sopenharmony_ci	wr_reg16(info, TCR, value);
132762306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
132862306a36Sopenharmony_ci	return 0;
132962306a36Sopenharmony_ci}
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci#if SYNCLINK_GENERIC_HDLC
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci/**
133462306a36Sopenharmony_ci * hdlcdev_attach - called by generic HDLC layer when protocol selected (PPP, frame relay, etc.)
133562306a36Sopenharmony_ci * @dev:      pointer to network device structure
133662306a36Sopenharmony_ci * @encoding: serial encoding setting
133762306a36Sopenharmony_ci * @parity:   FCS setting
133862306a36Sopenharmony_ci *
133962306a36Sopenharmony_ci * Set encoding and frame check sequence (FCS) options.
134062306a36Sopenharmony_ci *
134162306a36Sopenharmony_ci * Return: 0 if success, otherwise error code
134262306a36Sopenharmony_ci */
134362306a36Sopenharmony_cistatic int hdlcdev_attach(struct net_device *dev, unsigned short encoding,
134462306a36Sopenharmony_ci			  unsigned short parity)
134562306a36Sopenharmony_ci{
134662306a36Sopenharmony_ci	struct slgt_info *info = dev_to_port(dev);
134762306a36Sopenharmony_ci	unsigned char  new_encoding;
134862306a36Sopenharmony_ci	unsigned short new_crctype;
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci	/* return error if TTY interface open */
135162306a36Sopenharmony_ci	if (info->port.count)
135262306a36Sopenharmony_ci		return -EBUSY;
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci	DBGINFO(("%s hdlcdev_attach\n", info->device_name));
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci	switch (encoding)
135762306a36Sopenharmony_ci	{
135862306a36Sopenharmony_ci	case ENCODING_NRZ:        new_encoding = HDLC_ENCODING_NRZ; break;
135962306a36Sopenharmony_ci	case ENCODING_NRZI:       new_encoding = HDLC_ENCODING_NRZI_SPACE; break;
136062306a36Sopenharmony_ci	case ENCODING_FM_MARK:    new_encoding = HDLC_ENCODING_BIPHASE_MARK; break;
136162306a36Sopenharmony_ci	case ENCODING_FM_SPACE:   new_encoding = HDLC_ENCODING_BIPHASE_SPACE; break;
136262306a36Sopenharmony_ci	case ENCODING_MANCHESTER: new_encoding = HDLC_ENCODING_BIPHASE_LEVEL; break;
136362306a36Sopenharmony_ci	default: return -EINVAL;
136462306a36Sopenharmony_ci	}
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	switch (parity)
136762306a36Sopenharmony_ci	{
136862306a36Sopenharmony_ci	case PARITY_NONE:            new_crctype = HDLC_CRC_NONE; break;
136962306a36Sopenharmony_ci	case PARITY_CRC16_PR1_CCITT: new_crctype = HDLC_CRC_16_CCITT; break;
137062306a36Sopenharmony_ci	case PARITY_CRC32_PR1_CCITT: new_crctype = HDLC_CRC_32_CCITT; break;
137162306a36Sopenharmony_ci	default: return -EINVAL;
137262306a36Sopenharmony_ci	}
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	info->params.encoding = new_encoding;
137562306a36Sopenharmony_ci	info->params.crc_type = new_crctype;
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci	/* if network interface up, reprogram hardware */
137862306a36Sopenharmony_ci	if (info->netcount)
137962306a36Sopenharmony_ci		program_hw(info);
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci	return 0;
138262306a36Sopenharmony_ci}
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci/**
138562306a36Sopenharmony_ci * hdlcdev_xmit - called by generic HDLC layer to send a frame
138662306a36Sopenharmony_ci * @skb: socket buffer containing HDLC frame
138762306a36Sopenharmony_ci * @dev: pointer to network device structure
138862306a36Sopenharmony_ci */
138962306a36Sopenharmony_cistatic netdev_tx_t hdlcdev_xmit(struct sk_buff *skb,
139062306a36Sopenharmony_ci				      struct net_device *dev)
139162306a36Sopenharmony_ci{
139262306a36Sopenharmony_ci	struct slgt_info *info = dev_to_port(dev);
139362306a36Sopenharmony_ci	unsigned long flags;
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	DBGINFO(("%s hdlc_xmit\n", dev->name));
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci	if (!skb->len)
139862306a36Sopenharmony_ci		return NETDEV_TX_OK;
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	/* stop sending until this frame completes */
140162306a36Sopenharmony_ci	netif_stop_queue(dev);
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci	/* update network statistics */
140462306a36Sopenharmony_ci	dev->stats.tx_packets++;
140562306a36Sopenharmony_ci	dev->stats.tx_bytes += skb->len;
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci	/* save start time for transmit timeout detection */
140862306a36Sopenharmony_ci	netif_trans_update(dev);
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock, flags);
141162306a36Sopenharmony_ci	tx_load(info, skb->data, skb->len);
141262306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->lock, flags);
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	/* done with socket buffer, so free it */
141562306a36Sopenharmony_ci	dev_kfree_skb(skb);
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci	return NETDEV_TX_OK;
141862306a36Sopenharmony_ci}
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci/**
142162306a36Sopenharmony_ci * hdlcdev_open - called by network layer when interface enabled
142262306a36Sopenharmony_ci * @dev: pointer to network device structure
142362306a36Sopenharmony_ci *
142462306a36Sopenharmony_ci * Claim resources and initialize hardware.
142562306a36Sopenharmony_ci *
142662306a36Sopenharmony_ci * Return: 0 if success, otherwise error code
142762306a36Sopenharmony_ci */
142862306a36Sopenharmony_cistatic int hdlcdev_open(struct net_device *dev)
142962306a36Sopenharmony_ci{
143062306a36Sopenharmony_ci	struct slgt_info *info = dev_to_port(dev);
143162306a36Sopenharmony_ci	int rc;
143262306a36Sopenharmony_ci	unsigned long flags;
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci	DBGINFO(("%s hdlcdev_open\n", dev->name));
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci	/* arbitrate between network and tty opens */
143762306a36Sopenharmony_ci	spin_lock_irqsave(&info->netlock, flags);
143862306a36Sopenharmony_ci	if (info->port.count != 0 || info->netcount != 0) {
143962306a36Sopenharmony_ci		DBGINFO(("%s hdlc_open busy\n", dev->name));
144062306a36Sopenharmony_ci		spin_unlock_irqrestore(&info->netlock, flags);
144162306a36Sopenharmony_ci		return -EBUSY;
144262306a36Sopenharmony_ci	}
144362306a36Sopenharmony_ci	info->netcount=1;
144462306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->netlock, flags);
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ci	/* claim resources and init adapter */
144762306a36Sopenharmony_ci	if ((rc = startup(info)) != 0) {
144862306a36Sopenharmony_ci		spin_lock_irqsave(&info->netlock, flags);
144962306a36Sopenharmony_ci		info->netcount=0;
145062306a36Sopenharmony_ci		spin_unlock_irqrestore(&info->netlock, flags);
145162306a36Sopenharmony_ci		return rc;
145262306a36Sopenharmony_ci	}
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_ci	/* generic HDLC layer open processing */
145562306a36Sopenharmony_ci	rc = hdlc_open(dev);
145662306a36Sopenharmony_ci	if (rc) {
145762306a36Sopenharmony_ci		shutdown(info);
145862306a36Sopenharmony_ci		spin_lock_irqsave(&info->netlock, flags);
145962306a36Sopenharmony_ci		info->netcount = 0;
146062306a36Sopenharmony_ci		spin_unlock_irqrestore(&info->netlock, flags);
146162306a36Sopenharmony_ci		return rc;
146262306a36Sopenharmony_ci	}
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci	/* assert RTS and DTR, apply hardware settings */
146562306a36Sopenharmony_ci	info->signals |= SerialSignal_RTS | SerialSignal_DTR;
146662306a36Sopenharmony_ci	program_hw(info);
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci	/* enable network layer transmit */
146962306a36Sopenharmony_ci	netif_trans_update(dev);
147062306a36Sopenharmony_ci	netif_start_queue(dev);
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci	/* inform generic HDLC layer of current DCD status */
147362306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock, flags);
147462306a36Sopenharmony_ci	get_gtsignals(info);
147562306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->lock, flags);
147662306a36Sopenharmony_ci	if (info->signals & SerialSignal_DCD)
147762306a36Sopenharmony_ci		netif_carrier_on(dev);
147862306a36Sopenharmony_ci	else
147962306a36Sopenharmony_ci		netif_carrier_off(dev);
148062306a36Sopenharmony_ci	return 0;
148162306a36Sopenharmony_ci}
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_ci/**
148462306a36Sopenharmony_ci * hdlcdev_close - called by network layer when interface is disabled
148562306a36Sopenharmony_ci * @dev:  pointer to network device structure
148662306a36Sopenharmony_ci *
148762306a36Sopenharmony_ci * Shutdown hardware and release resources.
148862306a36Sopenharmony_ci *
148962306a36Sopenharmony_ci * Return: 0 if success, otherwise error code
149062306a36Sopenharmony_ci */
149162306a36Sopenharmony_cistatic int hdlcdev_close(struct net_device *dev)
149262306a36Sopenharmony_ci{
149362306a36Sopenharmony_ci	struct slgt_info *info = dev_to_port(dev);
149462306a36Sopenharmony_ci	unsigned long flags;
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	DBGINFO(("%s hdlcdev_close\n", dev->name));
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	netif_stop_queue(dev);
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci	/* shutdown adapter and release resources */
150162306a36Sopenharmony_ci	shutdown(info);
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_ci	hdlc_close(dev);
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci	spin_lock_irqsave(&info->netlock, flags);
150662306a36Sopenharmony_ci	info->netcount=0;
150762306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->netlock, flags);
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci	return 0;
151062306a36Sopenharmony_ci}
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci/**
151362306a36Sopenharmony_ci * hdlcdev_ioctl - called by network layer to process IOCTL call to network device
151462306a36Sopenharmony_ci * @dev: pointer to network device structure
151562306a36Sopenharmony_ci * @ifr: pointer to network interface request structure
151662306a36Sopenharmony_ci * @cmd: IOCTL command code
151762306a36Sopenharmony_ci *
151862306a36Sopenharmony_ci * Return: 0 if success, otherwise error code
151962306a36Sopenharmony_ci */
152062306a36Sopenharmony_cistatic int hdlcdev_ioctl(struct net_device *dev, struct if_settings *ifs)
152162306a36Sopenharmony_ci{
152262306a36Sopenharmony_ci	const size_t size = sizeof(sync_serial_settings);
152362306a36Sopenharmony_ci	sync_serial_settings new_line;
152462306a36Sopenharmony_ci	sync_serial_settings __user *line = ifs->ifs_ifsu.sync;
152562306a36Sopenharmony_ci	struct slgt_info *info = dev_to_port(dev);
152662306a36Sopenharmony_ci	unsigned int flags;
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ci	DBGINFO(("%s hdlcdev_ioctl\n", dev->name));
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci	/* return error if TTY interface open */
153162306a36Sopenharmony_ci	if (info->port.count)
153262306a36Sopenharmony_ci		return -EBUSY;
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci	memset(&new_line, 0, sizeof(new_line));
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_ci	switch (ifs->type) {
153762306a36Sopenharmony_ci	case IF_GET_IFACE: /* return current sync_serial_settings */
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci		ifs->type = IF_IFACE_SYNC_SERIAL;
154062306a36Sopenharmony_ci		if (ifs->size < size) {
154162306a36Sopenharmony_ci			ifs->size = size; /* data size wanted */
154262306a36Sopenharmony_ci			return -ENOBUFS;
154362306a36Sopenharmony_ci		}
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_ci		flags = info->params.flags & (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL |
154662306a36Sopenharmony_ci					      HDLC_FLAG_RXC_BRG    | HDLC_FLAG_RXC_TXCPIN |
154762306a36Sopenharmony_ci					      HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL |
154862306a36Sopenharmony_ci					      HDLC_FLAG_TXC_BRG    | HDLC_FLAG_TXC_RXCPIN);
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci		switch (flags){
155162306a36Sopenharmony_ci		case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN): new_line.clock_type = CLOCK_EXT; break;
155262306a36Sopenharmony_ci		case (HDLC_FLAG_RXC_BRG    | HDLC_FLAG_TXC_BRG):    new_line.clock_type = CLOCK_INT; break;
155362306a36Sopenharmony_ci		case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG):    new_line.clock_type = CLOCK_TXINT; break;
155462306a36Sopenharmony_ci		case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN): new_line.clock_type = CLOCK_TXFROMRX; break;
155562306a36Sopenharmony_ci		default: new_line.clock_type = CLOCK_DEFAULT;
155662306a36Sopenharmony_ci		}
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ci		new_line.clock_rate = info->params.clock_speed;
155962306a36Sopenharmony_ci		new_line.loopback   = info->params.loopback ? 1:0;
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_ci		if (copy_to_user(line, &new_line, size))
156262306a36Sopenharmony_ci			return -EFAULT;
156362306a36Sopenharmony_ci		return 0;
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_ci	case IF_IFACE_SYNC_SERIAL: /* set sync_serial_settings */
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci		if(!capable(CAP_NET_ADMIN))
156862306a36Sopenharmony_ci			return -EPERM;
156962306a36Sopenharmony_ci		if (copy_from_user(&new_line, line, size))
157062306a36Sopenharmony_ci			return -EFAULT;
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci		switch (new_line.clock_type)
157362306a36Sopenharmony_ci		{
157462306a36Sopenharmony_ci		case CLOCK_EXT:      flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN; break;
157562306a36Sopenharmony_ci		case CLOCK_TXFROMRX: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN; break;
157662306a36Sopenharmony_ci		case CLOCK_INT:      flags = HDLC_FLAG_RXC_BRG    | HDLC_FLAG_TXC_BRG;    break;
157762306a36Sopenharmony_ci		case CLOCK_TXINT:    flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG;    break;
157862306a36Sopenharmony_ci		case CLOCK_DEFAULT:  flags = info->params.flags &
157962306a36Sopenharmony_ci					     (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL |
158062306a36Sopenharmony_ci					      HDLC_FLAG_RXC_BRG    | HDLC_FLAG_RXC_TXCPIN |
158162306a36Sopenharmony_ci					      HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL |
158262306a36Sopenharmony_ci					      HDLC_FLAG_TXC_BRG    | HDLC_FLAG_TXC_RXCPIN); break;
158362306a36Sopenharmony_ci		default: return -EINVAL;
158462306a36Sopenharmony_ci		}
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_ci		if (new_line.loopback != 0 && new_line.loopback != 1)
158762306a36Sopenharmony_ci			return -EINVAL;
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci		info->params.flags &= ~(HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL |
159062306a36Sopenharmony_ci					HDLC_FLAG_RXC_BRG    | HDLC_FLAG_RXC_TXCPIN |
159162306a36Sopenharmony_ci					HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL |
159262306a36Sopenharmony_ci					HDLC_FLAG_TXC_BRG    | HDLC_FLAG_TXC_RXCPIN);
159362306a36Sopenharmony_ci		info->params.flags |= flags;
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_ci		info->params.loopback = new_line.loopback;
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_ci		if (flags & (HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG))
159862306a36Sopenharmony_ci			info->params.clock_speed = new_line.clock_rate;
159962306a36Sopenharmony_ci		else
160062306a36Sopenharmony_ci			info->params.clock_speed = 0;
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci		/* if network interface up, reprogram hardware */
160362306a36Sopenharmony_ci		if (info->netcount)
160462306a36Sopenharmony_ci			program_hw(info);
160562306a36Sopenharmony_ci		return 0;
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci	default:
160862306a36Sopenharmony_ci		return hdlc_ioctl(dev, ifs);
160962306a36Sopenharmony_ci	}
161062306a36Sopenharmony_ci}
161162306a36Sopenharmony_ci
161262306a36Sopenharmony_ci/**
161362306a36Sopenharmony_ci * hdlcdev_tx_timeout - called by network layer when transmit timeout is detected
161462306a36Sopenharmony_ci * @dev: pointer to network device structure
161562306a36Sopenharmony_ci * @txqueue: unused
161662306a36Sopenharmony_ci */
161762306a36Sopenharmony_cistatic void hdlcdev_tx_timeout(struct net_device *dev, unsigned int txqueue)
161862306a36Sopenharmony_ci{
161962306a36Sopenharmony_ci	struct slgt_info *info = dev_to_port(dev);
162062306a36Sopenharmony_ci	unsigned long flags;
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_ci	DBGINFO(("%s hdlcdev_tx_timeout\n", dev->name));
162362306a36Sopenharmony_ci
162462306a36Sopenharmony_ci	dev->stats.tx_errors++;
162562306a36Sopenharmony_ci	dev->stats.tx_aborted_errors++;
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
162862306a36Sopenharmony_ci	tx_stop(info);
162962306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
163062306a36Sopenharmony_ci
163162306a36Sopenharmony_ci	netif_wake_queue(dev);
163262306a36Sopenharmony_ci}
163362306a36Sopenharmony_ci
163462306a36Sopenharmony_ci/**
163562306a36Sopenharmony_ci * hdlcdev_tx_done - called by device driver when transmit completes
163662306a36Sopenharmony_ci * @info: pointer to device instance information
163762306a36Sopenharmony_ci *
163862306a36Sopenharmony_ci * Reenable network layer transmit if stopped.
163962306a36Sopenharmony_ci */
164062306a36Sopenharmony_cistatic void hdlcdev_tx_done(struct slgt_info *info)
164162306a36Sopenharmony_ci{
164262306a36Sopenharmony_ci	if (netif_queue_stopped(info->netdev))
164362306a36Sopenharmony_ci		netif_wake_queue(info->netdev);
164462306a36Sopenharmony_ci}
164562306a36Sopenharmony_ci
164662306a36Sopenharmony_ci/**
164762306a36Sopenharmony_ci * hdlcdev_rx - called by device driver when frame received
164862306a36Sopenharmony_ci * @info: pointer to device instance information
164962306a36Sopenharmony_ci * @buf:  pointer to buffer contianing frame data
165062306a36Sopenharmony_ci * @size: count of data bytes in buf
165162306a36Sopenharmony_ci *
165262306a36Sopenharmony_ci * Pass frame to network layer.
165362306a36Sopenharmony_ci */
165462306a36Sopenharmony_cistatic void hdlcdev_rx(struct slgt_info *info, char *buf, int size)
165562306a36Sopenharmony_ci{
165662306a36Sopenharmony_ci	struct sk_buff *skb = dev_alloc_skb(size);
165762306a36Sopenharmony_ci	struct net_device *dev = info->netdev;
165862306a36Sopenharmony_ci
165962306a36Sopenharmony_ci	DBGINFO(("%s hdlcdev_rx\n", dev->name));
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_ci	if (skb == NULL) {
166262306a36Sopenharmony_ci		DBGERR(("%s: can't alloc skb, drop packet\n", dev->name));
166362306a36Sopenharmony_ci		dev->stats.rx_dropped++;
166462306a36Sopenharmony_ci		return;
166562306a36Sopenharmony_ci	}
166662306a36Sopenharmony_ci
166762306a36Sopenharmony_ci	skb_put_data(skb, buf, size);
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_ci	skb->protocol = hdlc_type_trans(skb, dev);
167062306a36Sopenharmony_ci
167162306a36Sopenharmony_ci	dev->stats.rx_packets++;
167262306a36Sopenharmony_ci	dev->stats.rx_bytes += size;
167362306a36Sopenharmony_ci
167462306a36Sopenharmony_ci	netif_rx(skb);
167562306a36Sopenharmony_ci}
167662306a36Sopenharmony_ci
167762306a36Sopenharmony_cistatic const struct net_device_ops hdlcdev_ops = {
167862306a36Sopenharmony_ci	.ndo_open       = hdlcdev_open,
167962306a36Sopenharmony_ci	.ndo_stop       = hdlcdev_close,
168062306a36Sopenharmony_ci	.ndo_start_xmit = hdlc_start_xmit,
168162306a36Sopenharmony_ci	.ndo_siocwandev = hdlcdev_ioctl,
168262306a36Sopenharmony_ci	.ndo_tx_timeout = hdlcdev_tx_timeout,
168362306a36Sopenharmony_ci};
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_ci/**
168662306a36Sopenharmony_ci * hdlcdev_init - called by device driver when adding device instance
168762306a36Sopenharmony_ci * @info: pointer to device instance information
168862306a36Sopenharmony_ci *
168962306a36Sopenharmony_ci * Do generic HDLC initialization.
169062306a36Sopenharmony_ci *
169162306a36Sopenharmony_ci * Return: 0 if success, otherwise error code
169262306a36Sopenharmony_ci */
169362306a36Sopenharmony_cistatic int hdlcdev_init(struct slgt_info *info)
169462306a36Sopenharmony_ci{
169562306a36Sopenharmony_ci	int rc;
169662306a36Sopenharmony_ci	struct net_device *dev;
169762306a36Sopenharmony_ci	hdlc_device *hdlc;
169862306a36Sopenharmony_ci
169962306a36Sopenharmony_ci	/* allocate and initialize network and HDLC layer objects */
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_ci	dev = alloc_hdlcdev(info);
170262306a36Sopenharmony_ci	if (!dev) {
170362306a36Sopenharmony_ci		printk(KERN_ERR "%s hdlc device alloc failure\n", info->device_name);
170462306a36Sopenharmony_ci		return -ENOMEM;
170562306a36Sopenharmony_ci	}
170662306a36Sopenharmony_ci
170762306a36Sopenharmony_ci	/* for network layer reporting purposes only */
170862306a36Sopenharmony_ci	dev->mem_start = info->phys_reg_addr;
170962306a36Sopenharmony_ci	dev->mem_end   = info->phys_reg_addr + SLGT_REG_SIZE - 1;
171062306a36Sopenharmony_ci	dev->irq       = info->irq_level;
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci	/* network layer callbacks and settings */
171362306a36Sopenharmony_ci	dev->netdev_ops	    = &hdlcdev_ops;
171462306a36Sopenharmony_ci	dev->watchdog_timeo = 10 * HZ;
171562306a36Sopenharmony_ci	dev->tx_queue_len   = 50;
171662306a36Sopenharmony_ci
171762306a36Sopenharmony_ci	/* generic HDLC layer callbacks and settings */
171862306a36Sopenharmony_ci	hdlc         = dev_to_hdlc(dev);
171962306a36Sopenharmony_ci	hdlc->attach = hdlcdev_attach;
172062306a36Sopenharmony_ci	hdlc->xmit   = hdlcdev_xmit;
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_ci	/* register objects with HDLC layer */
172362306a36Sopenharmony_ci	rc = register_hdlc_device(dev);
172462306a36Sopenharmony_ci	if (rc) {
172562306a36Sopenharmony_ci		printk(KERN_WARNING "%s:unable to register hdlc device\n",__FILE__);
172662306a36Sopenharmony_ci		free_netdev(dev);
172762306a36Sopenharmony_ci		return rc;
172862306a36Sopenharmony_ci	}
172962306a36Sopenharmony_ci
173062306a36Sopenharmony_ci	info->netdev = dev;
173162306a36Sopenharmony_ci	return 0;
173262306a36Sopenharmony_ci}
173362306a36Sopenharmony_ci
173462306a36Sopenharmony_ci/**
173562306a36Sopenharmony_ci * hdlcdev_exit - called by device driver when removing device instance
173662306a36Sopenharmony_ci * @info: pointer to device instance information
173762306a36Sopenharmony_ci *
173862306a36Sopenharmony_ci * Do generic HDLC cleanup.
173962306a36Sopenharmony_ci */
174062306a36Sopenharmony_cistatic void hdlcdev_exit(struct slgt_info *info)
174162306a36Sopenharmony_ci{
174262306a36Sopenharmony_ci	if (!info->netdev)
174362306a36Sopenharmony_ci		return;
174462306a36Sopenharmony_ci	unregister_hdlc_device(info->netdev);
174562306a36Sopenharmony_ci	free_netdev(info->netdev);
174662306a36Sopenharmony_ci	info->netdev = NULL;
174762306a36Sopenharmony_ci}
174862306a36Sopenharmony_ci
174962306a36Sopenharmony_ci#endif /* ifdef CONFIG_HDLC */
175062306a36Sopenharmony_ci
175162306a36Sopenharmony_ci/*
175262306a36Sopenharmony_ci * get async data from rx DMA buffers
175362306a36Sopenharmony_ci */
175462306a36Sopenharmony_cistatic void rx_async(struct slgt_info *info)
175562306a36Sopenharmony_ci{
175662306a36Sopenharmony_ci 	struct mgsl_icount *icount = &info->icount;
175762306a36Sopenharmony_ci	unsigned int start, end;
175862306a36Sopenharmony_ci	unsigned char *p;
175962306a36Sopenharmony_ci	unsigned char status;
176062306a36Sopenharmony_ci	struct slgt_desc *bufs = info->rbufs;
176162306a36Sopenharmony_ci	int i, count;
176262306a36Sopenharmony_ci	int chars = 0;
176362306a36Sopenharmony_ci	int stat;
176462306a36Sopenharmony_ci	unsigned char ch;
176562306a36Sopenharmony_ci
176662306a36Sopenharmony_ci	start = end = info->rbuf_current;
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_ci	while(desc_complete(bufs[end])) {
176962306a36Sopenharmony_ci		count = desc_count(bufs[end]) - info->rbuf_index;
177062306a36Sopenharmony_ci		p     = bufs[end].buf + info->rbuf_index;
177162306a36Sopenharmony_ci
177262306a36Sopenharmony_ci		DBGISR(("%s rx_async count=%d\n", info->device_name, count));
177362306a36Sopenharmony_ci		DBGDATA(info, p, count, "rx");
177462306a36Sopenharmony_ci
177562306a36Sopenharmony_ci		for(i=0 ; i < count; i+=2, p+=2) {
177662306a36Sopenharmony_ci			ch = *p;
177762306a36Sopenharmony_ci			icount->rx++;
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_ci			stat = 0;
178062306a36Sopenharmony_ci
178162306a36Sopenharmony_ci			status = *(p + 1) & (BIT1 + BIT0);
178262306a36Sopenharmony_ci			if (status) {
178362306a36Sopenharmony_ci				if (status & BIT1)
178462306a36Sopenharmony_ci					icount->parity++;
178562306a36Sopenharmony_ci				else if (status & BIT0)
178662306a36Sopenharmony_ci					icount->frame++;
178762306a36Sopenharmony_ci				/* discard char if tty control flags say so */
178862306a36Sopenharmony_ci				if (status & info->ignore_status_mask)
178962306a36Sopenharmony_ci					continue;
179062306a36Sopenharmony_ci				if (status & BIT1)
179162306a36Sopenharmony_ci					stat = TTY_PARITY;
179262306a36Sopenharmony_ci				else if (status & BIT0)
179362306a36Sopenharmony_ci					stat = TTY_FRAME;
179462306a36Sopenharmony_ci			}
179562306a36Sopenharmony_ci			tty_insert_flip_char(&info->port, ch, stat);
179662306a36Sopenharmony_ci			chars++;
179762306a36Sopenharmony_ci		}
179862306a36Sopenharmony_ci
179962306a36Sopenharmony_ci		if (i < count) {
180062306a36Sopenharmony_ci			/* receive buffer not completed */
180162306a36Sopenharmony_ci			info->rbuf_index += i;
180262306a36Sopenharmony_ci			mod_timer(&info->rx_timer, jiffies + 1);
180362306a36Sopenharmony_ci			break;
180462306a36Sopenharmony_ci		}
180562306a36Sopenharmony_ci
180662306a36Sopenharmony_ci		info->rbuf_index = 0;
180762306a36Sopenharmony_ci		free_rbufs(info, end, end);
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_ci		if (++end == info->rbuf_count)
181062306a36Sopenharmony_ci			end = 0;
181162306a36Sopenharmony_ci
181262306a36Sopenharmony_ci		/* if entire list searched then no frame available */
181362306a36Sopenharmony_ci		if (end == start)
181462306a36Sopenharmony_ci			break;
181562306a36Sopenharmony_ci	}
181662306a36Sopenharmony_ci
181762306a36Sopenharmony_ci	if (chars)
181862306a36Sopenharmony_ci		tty_flip_buffer_push(&info->port);
181962306a36Sopenharmony_ci}
182062306a36Sopenharmony_ci
182162306a36Sopenharmony_ci/*
182262306a36Sopenharmony_ci * return next bottom half action to perform
182362306a36Sopenharmony_ci */
182462306a36Sopenharmony_cistatic int bh_action(struct slgt_info *info)
182562306a36Sopenharmony_ci{
182662306a36Sopenharmony_ci	unsigned long flags;
182762306a36Sopenharmony_ci	int rc;
182862306a36Sopenharmony_ci
182962306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
183062306a36Sopenharmony_ci
183162306a36Sopenharmony_ci	if (info->pending_bh & BH_RECEIVE) {
183262306a36Sopenharmony_ci		info->pending_bh &= ~BH_RECEIVE;
183362306a36Sopenharmony_ci		rc = BH_RECEIVE;
183462306a36Sopenharmony_ci	} else if (info->pending_bh & BH_TRANSMIT) {
183562306a36Sopenharmony_ci		info->pending_bh &= ~BH_TRANSMIT;
183662306a36Sopenharmony_ci		rc = BH_TRANSMIT;
183762306a36Sopenharmony_ci	} else if (info->pending_bh & BH_STATUS) {
183862306a36Sopenharmony_ci		info->pending_bh &= ~BH_STATUS;
183962306a36Sopenharmony_ci		rc = BH_STATUS;
184062306a36Sopenharmony_ci	} else {
184162306a36Sopenharmony_ci		/* Mark BH routine as complete */
184262306a36Sopenharmony_ci		info->bh_running = false;
184362306a36Sopenharmony_ci		info->bh_requested = false;
184462306a36Sopenharmony_ci		rc = 0;
184562306a36Sopenharmony_ci	}
184662306a36Sopenharmony_ci
184762306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
184862306a36Sopenharmony_ci
184962306a36Sopenharmony_ci	return rc;
185062306a36Sopenharmony_ci}
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_ci/*
185362306a36Sopenharmony_ci * perform bottom half processing
185462306a36Sopenharmony_ci */
185562306a36Sopenharmony_cistatic void bh_handler(struct work_struct *work)
185662306a36Sopenharmony_ci{
185762306a36Sopenharmony_ci	struct slgt_info *info = container_of(work, struct slgt_info, task);
185862306a36Sopenharmony_ci	int action;
185962306a36Sopenharmony_ci
186062306a36Sopenharmony_ci	info->bh_running = true;
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_ci	while((action = bh_action(info))) {
186362306a36Sopenharmony_ci		switch (action) {
186462306a36Sopenharmony_ci		case BH_RECEIVE:
186562306a36Sopenharmony_ci			DBGBH(("%s bh receive\n", info->device_name));
186662306a36Sopenharmony_ci			switch(info->params.mode) {
186762306a36Sopenharmony_ci			case MGSL_MODE_ASYNC:
186862306a36Sopenharmony_ci				rx_async(info);
186962306a36Sopenharmony_ci				break;
187062306a36Sopenharmony_ci			case MGSL_MODE_HDLC:
187162306a36Sopenharmony_ci				while(rx_get_frame(info));
187262306a36Sopenharmony_ci				break;
187362306a36Sopenharmony_ci			case MGSL_MODE_RAW:
187462306a36Sopenharmony_ci			case MGSL_MODE_MONOSYNC:
187562306a36Sopenharmony_ci			case MGSL_MODE_BISYNC:
187662306a36Sopenharmony_ci			case MGSL_MODE_XSYNC:
187762306a36Sopenharmony_ci				while(rx_get_buf(info));
187862306a36Sopenharmony_ci				break;
187962306a36Sopenharmony_ci			}
188062306a36Sopenharmony_ci			/* restart receiver if rx DMA buffers exhausted */
188162306a36Sopenharmony_ci			if (info->rx_restart)
188262306a36Sopenharmony_ci				rx_start(info);
188362306a36Sopenharmony_ci			break;
188462306a36Sopenharmony_ci		case BH_TRANSMIT:
188562306a36Sopenharmony_ci			bh_transmit(info);
188662306a36Sopenharmony_ci			break;
188762306a36Sopenharmony_ci		case BH_STATUS:
188862306a36Sopenharmony_ci			DBGBH(("%s bh status\n", info->device_name));
188962306a36Sopenharmony_ci			info->ri_chkcount = 0;
189062306a36Sopenharmony_ci			info->dsr_chkcount = 0;
189162306a36Sopenharmony_ci			info->dcd_chkcount = 0;
189262306a36Sopenharmony_ci			info->cts_chkcount = 0;
189362306a36Sopenharmony_ci			break;
189462306a36Sopenharmony_ci		default:
189562306a36Sopenharmony_ci			DBGBH(("%s unknown action\n", info->device_name));
189662306a36Sopenharmony_ci			break;
189762306a36Sopenharmony_ci		}
189862306a36Sopenharmony_ci	}
189962306a36Sopenharmony_ci	DBGBH(("%s bh_handler exit\n", info->device_name));
190062306a36Sopenharmony_ci}
190162306a36Sopenharmony_ci
190262306a36Sopenharmony_cistatic void bh_transmit(struct slgt_info *info)
190362306a36Sopenharmony_ci{
190462306a36Sopenharmony_ci	struct tty_struct *tty = info->port.tty;
190562306a36Sopenharmony_ci
190662306a36Sopenharmony_ci	DBGBH(("%s bh_transmit\n", info->device_name));
190762306a36Sopenharmony_ci	if (tty)
190862306a36Sopenharmony_ci		tty_wakeup(tty);
190962306a36Sopenharmony_ci}
191062306a36Sopenharmony_ci
191162306a36Sopenharmony_cistatic void dsr_change(struct slgt_info *info, unsigned short status)
191262306a36Sopenharmony_ci{
191362306a36Sopenharmony_ci	if (status & BIT3) {
191462306a36Sopenharmony_ci		info->signals |= SerialSignal_DSR;
191562306a36Sopenharmony_ci		info->input_signal_events.dsr_up++;
191662306a36Sopenharmony_ci	} else {
191762306a36Sopenharmony_ci		info->signals &= ~SerialSignal_DSR;
191862306a36Sopenharmony_ci		info->input_signal_events.dsr_down++;
191962306a36Sopenharmony_ci	}
192062306a36Sopenharmony_ci	DBGISR(("dsr_change %s signals=%04X\n", info->device_name, info->signals));
192162306a36Sopenharmony_ci	if ((info->dsr_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) {
192262306a36Sopenharmony_ci		slgt_irq_off(info, IRQ_DSR);
192362306a36Sopenharmony_ci		return;
192462306a36Sopenharmony_ci	}
192562306a36Sopenharmony_ci	info->icount.dsr++;
192662306a36Sopenharmony_ci	wake_up_interruptible(&info->status_event_wait_q);
192762306a36Sopenharmony_ci	wake_up_interruptible(&info->event_wait_q);
192862306a36Sopenharmony_ci	info->pending_bh |= BH_STATUS;
192962306a36Sopenharmony_ci}
193062306a36Sopenharmony_ci
193162306a36Sopenharmony_cistatic void cts_change(struct slgt_info *info, unsigned short status)
193262306a36Sopenharmony_ci{
193362306a36Sopenharmony_ci	if (status & BIT2) {
193462306a36Sopenharmony_ci		info->signals |= SerialSignal_CTS;
193562306a36Sopenharmony_ci		info->input_signal_events.cts_up++;
193662306a36Sopenharmony_ci	} else {
193762306a36Sopenharmony_ci		info->signals &= ~SerialSignal_CTS;
193862306a36Sopenharmony_ci		info->input_signal_events.cts_down++;
193962306a36Sopenharmony_ci	}
194062306a36Sopenharmony_ci	DBGISR(("cts_change %s signals=%04X\n", info->device_name, info->signals));
194162306a36Sopenharmony_ci	if ((info->cts_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) {
194262306a36Sopenharmony_ci		slgt_irq_off(info, IRQ_CTS);
194362306a36Sopenharmony_ci		return;
194462306a36Sopenharmony_ci	}
194562306a36Sopenharmony_ci	info->icount.cts++;
194662306a36Sopenharmony_ci	wake_up_interruptible(&info->status_event_wait_q);
194762306a36Sopenharmony_ci	wake_up_interruptible(&info->event_wait_q);
194862306a36Sopenharmony_ci	info->pending_bh |= BH_STATUS;
194962306a36Sopenharmony_ci
195062306a36Sopenharmony_ci	if (tty_port_cts_enabled(&info->port)) {
195162306a36Sopenharmony_ci		if (info->port.tty) {
195262306a36Sopenharmony_ci			if (info->port.tty->hw_stopped) {
195362306a36Sopenharmony_ci				if (info->signals & SerialSignal_CTS) {
195462306a36Sopenharmony_ci					info->port.tty->hw_stopped = false;
195562306a36Sopenharmony_ci					info->pending_bh |= BH_TRANSMIT;
195662306a36Sopenharmony_ci					return;
195762306a36Sopenharmony_ci				}
195862306a36Sopenharmony_ci			} else {
195962306a36Sopenharmony_ci				if (!(info->signals & SerialSignal_CTS))
196062306a36Sopenharmony_ci					info->port.tty->hw_stopped = true;
196162306a36Sopenharmony_ci			}
196262306a36Sopenharmony_ci		}
196362306a36Sopenharmony_ci	}
196462306a36Sopenharmony_ci}
196562306a36Sopenharmony_ci
196662306a36Sopenharmony_cistatic void dcd_change(struct slgt_info *info, unsigned short status)
196762306a36Sopenharmony_ci{
196862306a36Sopenharmony_ci	if (status & BIT1) {
196962306a36Sopenharmony_ci		info->signals |= SerialSignal_DCD;
197062306a36Sopenharmony_ci		info->input_signal_events.dcd_up++;
197162306a36Sopenharmony_ci	} else {
197262306a36Sopenharmony_ci		info->signals &= ~SerialSignal_DCD;
197362306a36Sopenharmony_ci		info->input_signal_events.dcd_down++;
197462306a36Sopenharmony_ci	}
197562306a36Sopenharmony_ci	DBGISR(("dcd_change %s signals=%04X\n", info->device_name, info->signals));
197662306a36Sopenharmony_ci	if ((info->dcd_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) {
197762306a36Sopenharmony_ci		slgt_irq_off(info, IRQ_DCD);
197862306a36Sopenharmony_ci		return;
197962306a36Sopenharmony_ci	}
198062306a36Sopenharmony_ci	info->icount.dcd++;
198162306a36Sopenharmony_ci#if SYNCLINK_GENERIC_HDLC
198262306a36Sopenharmony_ci	if (info->netcount) {
198362306a36Sopenharmony_ci		if (info->signals & SerialSignal_DCD)
198462306a36Sopenharmony_ci			netif_carrier_on(info->netdev);
198562306a36Sopenharmony_ci		else
198662306a36Sopenharmony_ci			netif_carrier_off(info->netdev);
198762306a36Sopenharmony_ci	}
198862306a36Sopenharmony_ci#endif
198962306a36Sopenharmony_ci	wake_up_interruptible(&info->status_event_wait_q);
199062306a36Sopenharmony_ci	wake_up_interruptible(&info->event_wait_q);
199162306a36Sopenharmony_ci	info->pending_bh |= BH_STATUS;
199262306a36Sopenharmony_ci
199362306a36Sopenharmony_ci	if (tty_port_check_carrier(&info->port)) {
199462306a36Sopenharmony_ci		if (info->signals & SerialSignal_DCD)
199562306a36Sopenharmony_ci			wake_up_interruptible(&info->port.open_wait);
199662306a36Sopenharmony_ci		else {
199762306a36Sopenharmony_ci			if (info->port.tty)
199862306a36Sopenharmony_ci				tty_hangup(info->port.tty);
199962306a36Sopenharmony_ci		}
200062306a36Sopenharmony_ci	}
200162306a36Sopenharmony_ci}
200262306a36Sopenharmony_ci
200362306a36Sopenharmony_cistatic void ri_change(struct slgt_info *info, unsigned short status)
200462306a36Sopenharmony_ci{
200562306a36Sopenharmony_ci	if (status & BIT0) {
200662306a36Sopenharmony_ci		info->signals |= SerialSignal_RI;
200762306a36Sopenharmony_ci		info->input_signal_events.ri_up++;
200862306a36Sopenharmony_ci	} else {
200962306a36Sopenharmony_ci		info->signals &= ~SerialSignal_RI;
201062306a36Sopenharmony_ci		info->input_signal_events.ri_down++;
201162306a36Sopenharmony_ci	}
201262306a36Sopenharmony_ci	DBGISR(("ri_change %s signals=%04X\n", info->device_name, info->signals));
201362306a36Sopenharmony_ci	if ((info->ri_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) {
201462306a36Sopenharmony_ci		slgt_irq_off(info, IRQ_RI);
201562306a36Sopenharmony_ci		return;
201662306a36Sopenharmony_ci	}
201762306a36Sopenharmony_ci	info->icount.rng++;
201862306a36Sopenharmony_ci	wake_up_interruptible(&info->status_event_wait_q);
201962306a36Sopenharmony_ci	wake_up_interruptible(&info->event_wait_q);
202062306a36Sopenharmony_ci	info->pending_bh |= BH_STATUS;
202162306a36Sopenharmony_ci}
202262306a36Sopenharmony_ci
202362306a36Sopenharmony_cistatic void isr_rxdata(struct slgt_info *info)
202462306a36Sopenharmony_ci{
202562306a36Sopenharmony_ci	unsigned int count = info->rbuf_fill_count;
202662306a36Sopenharmony_ci	unsigned int i = info->rbuf_fill_index;
202762306a36Sopenharmony_ci	unsigned short reg;
202862306a36Sopenharmony_ci
202962306a36Sopenharmony_ci	while (rd_reg16(info, SSR) & IRQ_RXDATA) {
203062306a36Sopenharmony_ci		reg = rd_reg16(info, RDR);
203162306a36Sopenharmony_ci		DBGISR(("isr_rxdata %s RDR=%04X\n", info->device_name, reg));
203262306a36Sopenharmony_ci		if (desc_complete(info->rbufs[i])) {
203362306a36Sopenharmony_ci			/* all buffers full */
203462306a36Sopenharmony_ci			rx_stop(info);
203562306a36Sopenharmony_ci			info->rx_restart = true;
203662306a36Sopenharmony_ci			continue;
203762306a36Sopenharmony_ci		}
203862306a36Sopenharmony_ci		info->rbufs[i].buf[count++] = (unsigned char)reg;
203962306a36Sopenharmony_ci		/* async mode saves status byte to buffer for each data byte */
204062306a36Sopenharmony_ci		if (info->params.mode == MGSL_MODE_ASYNC)
204162306a36Sopenharmony_ci			info->rbufs[i].buf[count++] = (unsigned char)(reg >> 8);
204262306a36Sopenharmony_ci		if (count == info->rbuf_fill_level || (reg & BIT10)) {
204362306a36Sopenharmony_ci			/* buffer full or end of frame */
204462306a36Sopenharmony_ci			set_desc_count(info->rbufs[i], count);
204562306a36Sopenharmony_ci			set_desc_status(info->rbufs[i], BIT15 | (reg >> 8));
204662306a36Sopenharmony_ci			info->rbuf_fill_count = count = 0;
204762306a36Sopenharmony_ci			if (++i == info->rbuf_count)
204862306a36Sopenharmony_ci				i = 0;
204962306a36Sopenharmony_ci			info->pending_bh |= BH_RECEIVE;
205062306a36Sopenharmony_ci		}
205162306a36Sopenharmony_ci	}
205262306a36Sopenharmony_ci
205362306a36Sopenharmony_ci	info->rbuf_fill_index = i;
205462306a36Sopenharmony_ci	info->rbuf_fill_count = count;
205562306a36Sopenharmony_ci}
205662306a36Sopenharmony_ci
205762306a36Sopenharmony_cistatic void isr_serial(struct slgt_info *info)
205862306a36Sopenharmony_ci{
205962306a36Sopenharmony_ci	unsigned short status = rd_reg16(info, SSR);
206062306a36Sopenharmony_ci
206162306a36Sopenharmony_ci	DBGISR(("%s isr_serial status=%04X\n", info->device_name, status));
206262306a36Sopenharmony_ci
206362306a36Sopenharmony_ci	wr_reg16(info, SSR, status); /* clear pending */
206462306a36Sopenharmony_ci
206562306a36Sopenharmony_ci	info->irq_occurred = true;
206662306a36Sopenharmony_ci
206762306a36Sopenharmony_ci	if (info->params.mode == MGSL_MODE_ASYNC) {
206862306a36Sopenharmony_ci		if (status & IRQ_TXIDLE) {
206962306a36Sopenharmony_ci			if (info->tx_active)
207062306a36Sopenharmony_ci				isr_txeom(info, status);
207162306a36Sopenharmony_ci		}
207262306a36Sopenharmony_ci		if (info->rx_pio && (status & IRQ_RXDATA))
207362306a36Sopenharmony_ci			isr_rxdata(info);
207462306a36Sopenharmony_ci		if ((status & IRQ_RXBREAK) && (status & RXBREAK)) {
207562306a36Sopenharmony_ci			info->icount.brk++;
207662306a36Sopenharmony_ci			/* process break detection if tty control allows */
207762306a36Sopenharmony_ci			if (info->port.tty) {
207862306a36Sopenharmony_ci				if (!(status & info->ignore_status_mask)) {
207962306a36Sopenharmony_ci					if (info->read_status_mask & MASK_BREAK) {
208062306a36Sopenharmony_ci						tty_insert_flip_char(&info->port, 0, TTY_BREAK);
208162306a36Sopenharmony_ci						if (info->port.flags & ASYNC_SAK)
208262306a36Sopenharmony_ci							do_SAK(info->port.tty);
208362306a36Sopenharmony_ci					}
208462306a36Sopenharmony_ci				}
208562306a36Sopenharmony_ci			}
208662306a36Sopenharmony_ci		}
208762306a36Sopenharmony_ci	} else {
208862306a36Sopenharmony_ci		if (status & (IRQ_TXIDLE + IRQ_TXUNDER))
208962306a36Sopenharmony_ci			isr_txeom(info, status);
209062306a36Sopenharmony_ci		if (info->rx_pio && (status & IRQ_RXDATA))
209162306a36Sopenharmony_ci			isr_rxdata(info);
209262306a36Sopenharmony_ci		if (status & IRQ_RXIDLE) {
209362306a36Sopenharmony_ci			if (status & RXIDLE)
209462306a36Sopenharmony_ci				info->icount.rxidle++;
209562306a36Sopenharmony_ci			else
209662306a36Sopenharmony_ci				info->icount.exithunt++;
209762306a36Sopenharmony_ci			wake_up_interruptible(&info->event_wait_q);
209862306a36Sopenharmony_ci		}
209962306a36Sopenharmony_ci
210062306a36Sopenharmony_ci		if (status & IRQ_RXOVER)
210162306a36Sopenharmony_ci			rx_start(info);
210262306a36Sopenharmony_ci	}
210362306a36Sopenharmony_ci
210462306a36Sopenharmony_ci	if (status & IRQ_DSR)
210562306a36Sopenharmony_ci		dsr_change(info, status);
210662306a36Sopenharmony_ci	if (status & IRQ_CTS)
210762306a36Sopenharmony_ci		cts_change(info, status);
210862306a36Sopenharmony_ci	if (status & IRQ_DCD)
210962306a36Sopenharmony_ci		dcd_change(info, status);
211062306a36Sopenharmony_ci	if (status & IRQ_RI)
211162306a36Sopenharmony_ci		ri_change(info, status);
211262306a36Sopenharmony_ci}
211362306a36Sopenharmony_ci
211462306a36Sopenharmony_cistatic void isr_rdma(struct slgt_info *info)
211562306a36Sopenharmony_ci{
211662306a36Sopenharmony_ci	unsigned int status = rd_reg32(info, RDCSR);
211762306a36Sopenharmony_ci
211862306a36Sopenharmony_ci	DBGISR(("%s isr_rdma status=%08x\n", info->device_name, status));
211962306a36Sopenharmony_ci
212062306a36Sopenharmony_ci	/* RDCSR (rx DMA control/status)
212162306a36Sopenharmony_ci	 *
212262306a36Sopenharmony_ci	 * 31..07  reserved
212362306a36Sopenharmony_ci	 * 06      save status byte to DMA buffer
212462306a36Sopenharmony_ci	 * 05      error
212562306a36Sopenharmony_ci	 * 04      eol (end of list)
212662306a36Sopenharmony_ci	 * 03      eob (end of buffer)
212762306a36Sopenharmony_ci	 * 02      IRQ enable
212862306a36Sopenharmony_ci	 * 01      reset
212962306a36Sopenharmony_ci	 * 00      enable
213062306a36Sopenharmony_ci	 */
213162306a36Sopenharmony_ci	wr_reg32(info, RDCSR, status);	/* clear pending */
213262306a36Sopenharmony_ci
213362306a36Sopenharmony_ci	if (status & (BIT5 + BIT4)) {
213462306a36Sopenharmony_ci		DBGISR(("%s isr_rdma rx_restart=1\n", info->device_name));
213562306a36Sopenharmony_ci		info->rx_restart = true;
213662306a36Sopenharmony_ci	}
213762306a36Sopenharmony_ci	info->pending_bh |= BH_RECEIVE;
213862306a36Sopenharmony_ci}
213962306a36Sopenharmony_ci
214062306a36Sopenharmony_cistatic void isr_tdma(struct slgt_info *info)
214162306a36Sopenharmony_ci{
214262306a36Sopenharmony_ci	unsigned int status = rd_reg32(info, TDCSR);
214362306a36Sopenharmony_ci
214462306a36Sopenharmony_ci	DBGISR(("%s isr_tdma status=%08x\n", info->device_name, status));
214562306a36Sopenharmony_ci
214662306a36Sopenharmony_ci	/* TDCSR (tx DMA control/status)
214762306a36Sopenharmony_ci	 *
214862306a36Sopenharmony_ci	 * 31..06  reserved
214962306a36Sopenharmony_ci	 * 05      error
215062306a36Sopenharmony_ci	 * 04      eol (end of list)
215162306a36Sopenharmony_ci	 * 03      eob (end of buffer)
215262306a36Sopenharmony_ci	 * 02      IRQ enable
215362306a36Sopenharmony_ci	 * 01      reset
215462306a36Sopenharmony_ci	 * 00      enable
215562306a36Sopenharmony_ci	 */
215662306a36Sopenharmony_ci	wr_reg32(info, TDCSR, status);	/* clear pending */
215762306a36Sopenharmony_ci
215862306a36Sopenharmony_ci	if (status & (BIT5 + BIT4 + BIT3)) {
215962306a36Sopenharmony_ci		// another transmit buffer has completed
216062306a36Sopenharmony_ci		// run bottom half to get more send data from user
216162306a36Sopenharmony_ci		info->pending_bh |= BH_TRANSMIT;
216262306a36Sopenharmony_ci	}
216362306a36Sopenharmony_ci}
216462306a36Sopenharmony_ci
216562306a36Sopenharmony_ci/*
216662306a36Sopenharmony_ci * return true if there are unsent tx DMA buffers, otherwise false
216762306a36Sopenharmony_ci *
216862306a36Sopenharmony_ci * if there are unsent buffers then info->tbuf_start
216962306a36Sopenharmony_ci * is set to index of first unsent buffer
217062306a36Sopenharmony_ci */
217162306a36Sopenharmony_cistatic bool unsent_tbufs(struct slgt_info *info)
217262306a36Sopenharmony_ci{
217362306a36Sopenharmony_ci	unsigned int i = info->tbuf_current;
217462306a36Sopenharmony_ci	bool rc = false;
217562306a36Sopenharmony_ci
217662306a36Sopenharmony_ci	/*
217762306a36Sopenharmony_ci	 * search backwards from last loaded buffer (precedes tbuf_current)
217862306a36Sopenharmony_ci	 * for first unsent buffer (desc_count > 0)
217962306a36Sopenharmony_ci	 */
218062306a36Sopenharmony_ci
218162306a36Sopenharmony_ci	do {
218262306a36Sopenharmony_ci		if (i)
218362306a36Sopenharmony_ci			i--;
218462306a36Sopenharmony_ci		else
218562306a36Sopenharmony_ci			i = info->tbuf_count - 1;
218662306a36Sopenharmony_ci		if (!desc_count(info->tbufs[i]))
218762306a36Sopenharmony_ci			break;
218862306a36Sopenharmony_ci		info->tbuf_start = i;
218962306a36Sopenharmony_ci		rc = true;
219062306a36Sopenharmony_ci	} while (i != info->tbuf_current);
219162306a36Sopenharmony_ci
219262306a36Sopenharmony_ci	return rc;
219362306a36Sopenharmony_ci}
219462306a36Sopenharmony_ci
219562306a36Sopenharmony_cistatic void isr_txeom(struct slgt_info *info, unsigned short status)
219662306a36Sopenharmony_ci{
219762306a36Sopenharmony_ci	DBGISR(("%s txeom status=%04x\n", info->device_name, status));
219862306a36Sopenharmony_ci
219962306a36Sopenharmony_ci	slgt_irq_off(info, IRQ_TXDATA + IRQ_TXIDLE + IRQ_TXUNDER);
220062306a36Sopenharmony_ci	tdma_reset(info);
220162306a36Sopenharmony_ci	if (status & IRQ_TXUNDER) {
220262306a36Sopenharmony_ci		unsigned short val = rd_reg16(info, TCR);
220362306a36Sopenharmony_ci		wr_reg16(info, TCR, (unsigned short)(val | BIT2)); /* set reset bit */
220462306a36Sopenharmony_ci		wr_reg16(info, TCR, val); /* clear reset bit */
220562306a36Sopenharmony_ci	}
220662306a36Sopenharmony_ci
220762306a36Sopenharmony_ci	if (info->tx_active) {
220862306a36Sopenharmony_ci		if (info->params.mode != MGSL_MODE_ASYNC) {
220962306a36Sopenharmony_ci			if (status & IRQ_TXUNDER)
221062306a36Sopenharmony_ci				info->icount.txunder++;
221162306a36Sopenharmony_ci			else if (status & IRQ_TXIDLE)
221262306a36Sopenharmony_ci				info->icount.txok++;
221362306a36Sopenharmony_ci		}
221462306a36Sopenharmony_ci
221562306a36Sopenharmony_ci		if (unsent_tbufs(info)) {
221662306a36Sopenharmony_ci			tx_start(info);
221762306a36Sopenharmony_ci			update_tx_timer(info);
221862306a36Sopenharmony_ci			return;
221962306a36Sopenharmony_ci		}
222062306a36Sopenharmony_ci		info->tx_active = false;
222162306a36Sopenharmony_ci
222262306a36Sopenharmony_ci		del_timer(&info->tx_timer);
222362306a36Sopenharmony_ci
222462306a36Sopenharmony_ci		if (info->params.mode != MGSL_MODE_ASYNC && info->drop_rts_on_tx_done) {
222562306a36Sopenharmony_ci			info->signals &= ~SerialSignal_RTS;
222662306a36Sopenharmony_ci			info->drop_rts_on_tx_done = false;
222762306a36Sopenharmony_ci			set_gtsignals(info);
222862306a36Sopenharmony_ci		}
222962306a36Sopenharmony_ci
223062306a36Sopenharmony_ci#if SYNCLINK_GENERIC_HDLC
223162306a36Sopenharmony_ci		if (info->netcount)
223262306a36Sopenharmony_ci			hdlcdev_tx_done(info);
223362306a36Sopenharmony_ci		else
223462306a36Sopenharmony_ci#endif
223562306a36Sopenharmony_ci		{
223662306a36Sopenharmony_ci			if (info->port.tty && (info->port.tty->flow.stopped || info->port.tty->hw_stopped)) {
223762306a36Sopenharmony_ci				tx_stop(info);
223862306a36Sopenharmony_ci				return;
223962306a36Sopenharmony_ci			}
224062306a36Sopenharmony_ci			info->pending_bh |= BH_TRANSMIT;
224162306a36Sopenharmony_ci		}
224262306a36Sopenharmony_ci	}
224362306a36Sopenharmony_ci}
224462306a36Sopenharmony_ci
224562306a36Sopenharmony_cistatic void isr_gpio(struct slgt_info *info, unsigned int changed, unsigned int state)
224662306a36Sopenharmony_ci{
224762306a36Sopenharmony_ci	struct cond_wait *w, *prev;
224862306a36Sopenharmony_ci
224962306a36Sopenharmony_ci	/* wake processes waiting for specific transitions */
225062306a36Sopenharmony_ci	for (w = info->gpio_wait_q, prev = NULL ; w != NULL ; w = w->next) {
225162306a36Sopenharmony_ci		if (w->data & changed) {
225262306a36Sopenharmony_ci			w->data = state;
225362306a36Sopenharmony_ci			wake_up_interruptible(&w->q);
225462306a36Sopenharmony_ci			if (prev != NULL)
225562306a36Sopenharmony_ci				prev->next = w->next;
225662306a36Sopenharmony_ci			else
225762306a36Sopenharmony_ci				info->gpio_wait_q = w->next;
225862306a36Sopenharmony_ci		} else
225962306a36Sopenharmony_ci			prev = w;
226062306a36Sopenharmony_ci	}
226162306a36Sopenharmony_ci}
226262306a36Sopenharmony_ci
226362306a36Sopenharmony_ci/* interrupt service routine
226462306a36Sopenharmony_ci *
226562306a36Sopenharmony_ci * 	irq	interrupt number
226662306a36Sopenharmony_ci * 	dev_id	device ID supplied during interrupt registration
226762306a36Sopenharmony_ci */
226862306a36Sopenharmony_cistatic irqreturn_t slgt_interrupt(int dummy, void *dev_id)
226962306a36Sopenharmony_ci{
227062306a36Sopenharmony_ci	struct slgt_info *info = dev_id;
227162306a36Sopenharmony_ci	unsigned int gsr;
227262306a36Sopenharmony_ci	unsigned int i;
227362306a36Sopenharmony_ci
227462306a36Sopenharmony_ci	DBGISR(("slgt_interrupt irq=%d entry\n", info->irq_level));
227562306a36Sopenharmony_ci
227662306a36Sopenharmony_ci	while((gsr = rd_reg32(info, GSR) & 0xffffff00)) {
227762306a36Sopenharmony_ci		DBGISR(("%s gsr=%08x\n", info->device_name, gsr));
227862306a36Sopenharmony_ci		info->irq_occurred = true;
227962306a36Sopenharmony_ci		for(i=0; i < info->port_count ; i++) {
228062306a36Sopenharmony_ci			if (info->port_array[i] == NULL)
228162306a36Sopenharmony_ci				continue;
228262306a36Sopenharmony_ci			spin_lock(&info->port_array[i]->lock);
228362306a36Sopenharmony_ci			if (gsr & (BIT8 << i))
228462306a36Sopenharmony_ci				isr_serial(info->port_array[i]);
228562306a36Sopenharmony_ci			if (gsr & (BIT16 << (i*2)))
228662306a36Sopenharmony_ci				isr_rdma(info->port_array[i]);
228762306a36Sopenharmony_ci			if (gsr & (BIT17 << (i*2)))
228862306a36Sopenharmony_ci				isr_tdma(info->port_array[i]);
228962306a36Sopenharmony_ci			spin_unlock(&info->port_array[i]->lock);
229062306a36Sopenharmony_ci		}
229162306a36Sopenharmony_ci	}
229262306a36Sopenharmony_ci
229362306a36Sopenharmony_ci	if (info->gpio_present) {
229462306a36Sopenharmony_ci		unsigned int state;
229562306a36Sopenharmony_ci		unsigned int changed;
229662306a36Sopenharmony_ci		spin_lock(&info->lock);
229762306a36Sopenharmony_ci		while ((changed = rd_reg32(info, IOSR)) != 0) {
229862306a36Sopenharmony_ci			DBGISR(("%s iosr=%08x\n", info->device_name, changed));
229962306a36Sopenharmony_ci			/* read latched state of GPIO signals */
230062306a36Sopenharmony_ci			state = rd_reg32(info, IOVR);
230162306a36Sopenharmony_ci			/* clear pending GPIO interrupt bits */
230262306a36Sopenharmony_ci			wr_reg32(info, IOSR, changed);
230362306a36Sopenharmony_ci			for (i=0 ; i < info->port_count ; i++) {
230462306a36Sopenharmony_ci				if (info->port_array[i] != NULL)
230562306a36Sopenharmony_ci					isr_gpio(info->port_array[i], changed, state);
230662306a36Sopenharmony_ci			}
230762306a36Sopenharmony_ci		}
230862306a36Sopenharmony_ci		spin_unlock(&info->lock);
230962306a36Sopenharmony_ci	}
231062306a36Sopenharmony_ci
231162306a36Sopenharmony_ci	for(i=0; i < info->port_count ; i++) {
231262306a36Sopenharmony_ci		struct slgt_info *port = info->port_array[i];
231362306a36Sopenharmony_ci		if (port == NULL)
231462306a36Sopenharmony_ci			continue;
231562306a36Sopenharmony_ci		spin_lock(&port->lock);
231662306a36Sopenharmony_ci		if ((port->port.count || port->netcount) &&
231762306a36Sopenharmony_ci		    port->pending_bh && !port->bh_running &&
231862306a36Sopenharmony_ci		    !port->bh_requested) {
231962306a36Sopenharmony_ci			DBGISR(("%s bh queued\n", port->device_name));
232062306a36Sopenharmony_ci			schedule_work(&port->task);
232162306a36Sopenharmony_ci			port->bh_requested = true;
232262306a36Sopenharmony_ci		}
232362306a36Sopenharmony_ci		spin_unlock(&port->lock);
232462306a36Sopenharmony_ci	}
232562306a36Sopenharmony_ci
232662306a36Sopenharmony_ci	DBGISR(("slgt_interrupt irq=%d exit\n", info->irq_level));
232762306a36Sopenharmony_ci	return IRQ_HANDLED;
232862306a36Sopenharmony_ci}
232962306a36Sopenharmony_ci
233062306a36Sopenharmony_cistatic int startup(struct slgt_info *info)
233162306a36Sopenharmony_ci{
233262306a36Sopenharmony_ci	DBGINFO(("%s startup\n", info->device_name));
233362306a36Sopenharmony_ci
233462306a36Sopenharmony_ci	if (tty_port_initialized(&info->port))
233562306a36Sopenharmony_ci		return 0;
233662306a36Sopenharmony_ci
233762306a36Sopenharmony_ci	if (!info->tx_buf) {
233862306a36Sopenharmony_ci		info->tx_buf = kmalloc(info->max_frame_size, GFP_KERNEL);
233962306a36Sopenharmony_ci		if (!info->tx_buf) {
234062306a36Sopenharmony_ci			DBGERR(("%s can't allocate tx buffer\n", info->device_name));
234162306a36Sopenharmony_ci			return -ENOMEM;
234262306a36Sopenharmony_ci		}
234362306a36Sopenharmony_ci	}
234462306a36Sopenharmony_ci
234562306a36Sopenharmony_ci	info->pending_bh = 0;
234662306a36Sopenharmony_ci
234762306a36Sopenharmony_ci	memset(&info->icount, 0, sizeof(info->icount));
234862306a36Sopenharmony_ci
234962306a36Sopenharmony_ci	/* program hardware for current parameters */
235062306a36Sopenharmony_ci	change_params(info);
235162306a36Sopenharmony_ci
235262306a36Sopenharmony_ci	if (info->port.tty)
235362306a36Sopenharmony_ci		clear_bit(TTY_IO_ERROR, &info->port.tty->flags);
235462306a36Sopenharmony_ci
235562306a36Sopenharmony_ci	tty_port_set_initialized(&info->port, true);
235662306a36Sopenharmony_ci
235762306a36Sopenharmony_ci	return 0;
235862306a36Sopenharmony_ci}
235962306a36Sopenharmony_ci
236062306a36Sopenharmony_ci/*
236162306a36Sopenharmony_ci *  called by close() and hangup() to shutdown hardware
236262306a36Sopenharmony_ci */
236362306a36Sopenharmony_cistatic void shutdown(struct slgt_info *info)
236462306a36Sopenharmony_ci{
236562306a36Sopenharmony_ci	unsigned long flags;
236662306a36Sopenharmony_ci
236762306a36Sopenharmony_ci	if (!tty_port_initialized(&info->port))
236862306a36Sopenharmony_ci		return;
236962306a36Sopenharmony_ci
237062306a36Sopenharmony_ci	DBGINFO(("%s shutdown\n", info->device_name));
237162306a36Sopenharmony_ci
237262306a36Sopenharmony_ci	/* clear status wait queue because status changes */
237362306a36Sopenharmony_ci	/* can't happen after shutting down the hardware */
237462306a36Sopenharmony_ci	wake_up_interruptible(&info->status_event_wait_q);
237562306a36Sopenharmony_ci	wake_up_interruptible(&info->event_wait_q);
237662306a36Sopenharmony_ci
237762306a36Sopenharmony_ci	del_timer_sync(&info->tx_timer);
237862306a36Sopenharmony_ci	del_timer_sync(&info->rx_timer);
237962306a36Sopenharmony_ci
238062306a36Sopenharmony_ci	kfree(info->tx_buf);
238162306a36Sopenharmony_ci	info->tx_buf = NULL;
238262306a36Sopenharmony_ci
238362306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
238462306a36Sopenharmony_ci
238562306a36Sopenharmony_ci	tx_stop(info);
238662306a36Sopenharmony_ci	rx_stop(info);
238762306a36Sopenharmony_ci
238862306a36Sopenharmony_ci	slgt_irq_off(info, IRQ_ALL | IRQ_MASTER);
238962306a36Sopenharmony_ci
239062306a36Sopenharmony_ci 	if (!info->port.tty || info->port.tty->termios.c_cflag & HUPCL) {
239162306a36Sopenharmony_ci		info->signals &= ~(SerialSignal_RTS | SerialSignal_DTR);
239262306a36Sopenharmony_ci		set_gtsignals(info);
239362306a36Sopenharmony_ci	}
239462306a36Sopenharmony_ci
239562306a36Sopenharmony_ci	flush_cond_wait(&info->gpio_wait_q);
239662306a36Sopenharmony_ci
239762306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
239862306a36Sopenharmony_ci
239962306a36Sopenharmony_ci	if (info->port.tty)
240062306a36Sopenharmony_ci		set_bit(TTY_IO_ERROR, &info->port.tty->flags);
240162306a36Sopenharmony_ci
240262306a36Sopenharmony_ci	tty_port_set_initialized(&info->port, false);
240362306a36Sopenharmony_ci}
240462306a36Sopenharmony_ci
240562306a36Sopenharmony_cistatic void program_hw(struct slgt_info *info)
240662306a36Sopenharmony_ci{
240762306a36Sopenharmony_ci	unsigned long flags;
240862306a36Sopenharmony_ci
240962306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
241062306a36Sopenharmony_ci
241162306a36Sopenharmony_ci	rx_stop(info);
241262306a36Sopenharmony_ci	tx_stop(info);
241362306a36Sopenharmony_ci
241462306a36Sopenharmony_ci	if (info->params.mode != MGSL_MODE_ASYNC ||
241562306a36Sopenharmony_ci	    info->netcount)
241662306a36Sopenharmony_ci		sync_mode(info);
241762306a36Sopenharmony_ci	else
241862306a36Sopenharmony_ci		async_mode(info);
241962306a36Sopenharmony_ci
242062306a36Sopenharmony_ci	set_gtsignals(info);
242162306a36Sopenharmony_ci
242262306a36Sopenharmony_ci	info->dcd_chkcount = 0;
242362306a36Sopenharmony_ci	info->cts_chkcount = 0;
242462306a36Sopenharmony_ci	info->ri_chkcount = 0;
242562306a36Sopenharmony_ci	info->dsr_chkcount = 0;
242662306a36Sopenharmony_ci
242762306a36Sopenharmony_ci	slgt_irq_on(info, IRQ_DCD | IRQ_CTS | IRQ_DSR | IRQ_RI);
242862306a36Sopenharmony_ci	get_gtsignals(info);
242962306a36Sopenharmony_ci
243062306a36Sopenharmony_ci	if (info->netcount ||
243162306a36Sopenharmony_ci	    (info->port.tty && info->port.tty->termios.c_cflag & CREAD))
243262306a36Sopenharmony_ci		rx_start(info);
243362306a36Sopenharmony_ci
243462306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
243562306a36Sopenharmony_ci}
243662306a36Sopenharmony_ci
243762306a36Sopenharmony_ci/*
243862306a36Sopenharmony_ci * reconfigure adapter based on new parameters
243962306a36Sopenharmony_ci */
244062306a36Sopenharmony_cistatic void change_params(struct slgt_info *info)
244162306a36Sopenharmony_ci{
244262306a36Sopenharmony_ci	unsigned cflag;
244362306a36Sopenharmony_ci	int bits_per_char;
244462306a36Sopenharmony_ci
244562306a36Sopenharmony_ci	if (!info->port.tty)
244662306a36Sopenharmony_ci		return;
244762306a36Sopenharmony_ci	DBGINFO(("%s change_params\n", info->device_name));
244862306a36Sopenharmony_ci
244962306a36Sopenharmony_ci	cflag = info->port.tty->termios.c_cflag;
245062306a36Sopenharmony_ci
245162306a36Sopenharmony_ci	/* if B0 rate (hangup) specified then negate RTS and DTR */
245262306a36Sopenharmony_ci	/* otherwise assert RTS and DTR */
245362306a36Sopenharmony_ci 	if (cflag & CBAUD)
245462306a36Sopenharmony_ci		info->signals |= SerialSignal_RTS | SerialSignal_DTR;
245562306a36Sopenharmony_ci	else
245662306a36Sopenharmony_ci		info->signals &= ~(SerialSignal_RTS | SerialSignal_DTR);
245762306a36Sopenharmony_ci
245862306a36Sopenharmony_ci	/* byte size and parity */
245962306a36Sopenharmony_ci
246062306a36Sopenharmony_ci	info->params.data_bits = tty_get_char_size(cflag);
246162306a36Sopenharmony_ci	info->params.stop_bits = (cflag & CSTOPB) ? 2 : 1;
246262306a36Sopenharmony_ci
246362306a36Sopenharmony_ci	if (cflag & PARENB)
246462306a36Sopenharmony_ci		info->params.parity = (cflag & PARODD) ? ASYNC_PARITY_ODD : ASYNC_PARITY_EVEN;
246562306a36Sopenharmony_ci	else
246662306a36Sopenharmony_ci		info->params.parity = ASYNC_PARITY_NONE;
246762306a36Sopenharmony_ci
246862306a36Sopenharmony_ci	/* calculate number of jiffies to transmit a full
246962306a36Sopenharmony_ci	 * FIFO (32 bytes) at specified data rate
247062306a36Sopenharmony_ci	 */
247162306a36Sopenharmony_ci	bits_per_char = info->params.data_bits +
247262306a36Sopenharmony_ci			info->params.stop_bits + 1;
247362306a36Sopenharmony_ci
247462306a36Sopenharmony_ci	info->params.data_rate = tty_get_baud_rate(info->port.tty);
247562306a36Sopenharmony_ci
247662306a36Sopenharmony_ci	if (info->params.data_rate) {
247762306a36Sopenharmony_ci		info->timeout = (32*HZ*bits_per_char) /
247862306a36Sopenharmony_ci				info->params.data_rate;
247962306a36Sopenharmony_ci	}
248062306a36Sopenharmony_ci	info->timeout += HZ/50;		/* Add .02 seconds of slop */
248162306a36Sopenharmony_ci
248262306a36Sopenharmony_ci	tty_port_set_cts_flow(&info->port, cflag & CRTSCTS);
248362306a36Sopenharmony_ci	tty_port_set_check_carrier(&info->port, ~cflag & CLOCAL);
248462306a36Sopenharmony_ci
248562306a36Sopenharmony_ci	/* process tty input control flags */
248662306a36Sopenharmony_ci
248762306a36Sopenharmony_ci	info->read_status_mask = IRQ_RXOVER;
248862306a36Sopenharmony_ci	if (I_INPCK(info->port.tty))
248962306a36Sopenharmony_ci		info->read_status_mask |= MASK_PARITY | MASK_FRAMING;
249062306a36Sopenharmony_ci	if (I_BRKINT(info->port.tty) || I_PARMRK(info->port.tty))
249162306a36Sopenharmony_ci		info->read_status_mask |= MASK_BREAK;
249262306a36Sopenharmony_ci	if (I_IGNPAR(info->port.tty))
249362306a36Sopenharmony_ci		info->ignore_status_mask |= MASK_PARITY | MASK_FRAMING;
249462306a36Sopenharmony_ci	if (I_IGNBRK(info->port.tty)) {
249562306a36Sopenharmony_ci		info->ignore_status_mask |= MASK_BREAK;
249662306a36Sopenharmony_ci		/* If ignoring parity and break indicators, ignore
249762306a36Sopenharmony_ci		 * overruns too.  (For real raw support).
249862306a36Sopenharmony_ci		 */
249962306a36Sopenharmony_ci		if (I_IGNPAR(info->port.tty))
250062306a36Sopenharmony_ci			info->ignore_status_mask |= MASK_OVERRUN;
250162306a36Sopenharmony_ci	}
250262306a36Sopenharmony_ci
250362306a36Sopenharmony_ci	program_hw(info);
250462306a36Sopenharmony_ci}
250562306a36Sopenharmony_ci
250662306a36Sopenharmony_cistatic int get_stats(struct slgt_info *info, struct mgsl_icount __user *user_icount)
250762306a36Sopenharmony_ci{
250862306a36Sopenharmony_ci	DBGINFO(("%s get_stats\n",  info->device_name));
250962306a36Sopenharmony_ci	if (!user_icount) {
251062306a36Sopenharmony_ci		memset(&info->icount, 0, sizeof(info->icount));
251162306a36Sopenharmony_ci	} else {
251262306a36Sopenharmony_ci		if (copy_to_user(user_icount, &info->icount, sizeof(struct mgsl_icount)))
251362306a36Sopenharmony_ci			return -EFAULT;
251462306a36Sopenharmony_ci	}
251562306a36Sopenharmony_ci	return 0;
251662306a36Sopenharmony_ci}
251762306a36Sopenharmony_ci
251862306a36Sopenharmony_cistatic int get_params(struct slgt_info *info, MGSL_PARAMS __user *user_params)
251962306a36Sopenharmony_ci{
252062306a36Sopenharmony_ci	DBGINFO(("%s get_params\n", info->device_name));
252162306a36Sopenharmony_ci	if (copy_to_user(user_params, &info->params, sizeof(MGSL_PARAMS)))
252262306a36Sopenharmony_ci		return -EFAULT;
252362306a36Sopenharmony_ci	return 0;
252462306a36Sopenharmony_ci}
252562306a36Sopenharmony_ci
252662306a36Sopenharmony_cistatic int set_params(struct slgt_info *info, MGSL_PARAMS __user *new_params)
252762306a36Sopenharmony_ci{
252862306a36Sopenharmony_ci 	unsigned long flags;
252962306a36Sopenharmony_ci	MGSL_PARAMS tmp_params;
253062306a36Sopenharmony_ci
253162306a36Sopenharmony_ci	DBGINFO(("%s set_params\n", info->device_name));
253262306a36Sopenharmony_ci	if (copy_from_user(&tmp_params, new_params, sizeof(MGSL_PARAMS)))
253362306a36Sopenharmony_ci		return -EFAULT;
253462306a36Sopenharmony_ci
253562306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock, flags);
253662306a36Sopenharmony_ci	if (tmp_params.mode == MGSL_MODE_BASE_CLOCK)
253762306a36Sopenharmony_ci		info->base_clock = tmp_params.clock_speed;
253862306a36Sopenharmony_ci	else
253962306a36Sopenharmony_ci		memcpy(&info->params, &tmp_params, sizeof(MGSL_PARAMS));
254062306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->lock, flags);
254162306a36Sopenharmony_ci
254262306a36Sopenharmony_ci	program_hw(info);
254362306a36Sopenharmony_ci
254462306a36Sopenharmony_ci	return 0;
254562306a36Sopenharmony_ci}
254662306a36Sopenharmony_ci
254762306a36Sopenharmony_cistatic int get_txidle(struct slgt_info *info, int __user *idle_mode)
254862306a36Sopenharmony_ci{
254962306a36Sopenharmony_ci	DBGINFO(("%s get_txidle=%d\n", info->device_name, info->idle_mode));
255062306a36Sopenharmony_ci	if (put_user(info->idle_mode, idle_mode))
255162306a36Sopenharmony_ci		return -EFAULT;
255262306a36Sopenharmony_ci	return 0;
255362306a36Sopenharmony_ci}
255462306a36Sopenharmony_ci
255562306a36Sopenharmony_cistatic int set_txidle(struct slgt_info *info, int idle_mode)
255662306a36Sopenharmony_ci{
255762306a36Sopenharmony_ci 	unsigned long flags;
255862306a36Sopenharmony_ci	DBGINFO(("%s set_txidle(%d)\n", info->device_name, idle_mode));
255962306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
256062306a36Sopenharmony_ci	info->idle_mode = idle_mode;
256162306a36Sopenharmony_ci	if (info->params.mode != MGSL_MODE_ASYNC)
256262306a36Sopenharmony_ci		tx_set_idle(info);
256362306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
256462306a36Sopenharmony_ci	return 0;
256562306a36Sopenharmony_ci}
256662306a36Sopenharmony_ci
256762306a36Sopenharmony_cistatic int tx_enable(struct slgt_info *info, int enable)
256862306a36Sopenharmony_ci{
256962306a36Sopenharmony_ci 	unsigned long flags;
257062306a36Sopenharmony_ci	DBGINFO(("%s tx_enable(%d)\n", info->device_name, enable));
257162306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
257262306a36Sopenharmony_ci	if (enable) {
257362306a36Sopenharmony_ci		if (!info->tx_enabled)
257462306a36Sopenharmony_ci			tx_start(info);
257562306a36Sopenharmony_ci	} else {
257662306a36Sopenharmony_ci		if (info->tx_enabled)
257762306a36Sopenharmony_ci			tx_stop(info);
257862306a36Sopenharmony_ci	}
257962306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
258062306a36Sopenharmony_ci	return 0;
258162306a36Sopenharmony_ci}
258262306a36Sopenharmony_ci
258362306a36Sopenharmony_ci/*
258462306a36Sopenharmony_ci * abort transmit HDLC frame
258562306a36Sopenharmony_ci */
258662306a36Sopenharmony_cistatic int tx_abort(struct slgt_info *info)
258762306a36Sopenharmony_ci{
258862306a36Sopenharmony_ci 	unsigned long flags;
258962306a36Sopenharmony_ci	DBGINFO(("%s tx_abort\n", info->device_name));
259062306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
259162306a36Sopenharmony_ci	tdma_reset(info);
259262306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
259362306a36Sopenharmony_ci	return 0;
259462306a36Sopenharmony_ci}
259562306a36Sopenharmony_ci
259662306a36Sopenharmony_cistatic int rx_enable(struct slgt_info *info, int enable)
259762306a36Sopenharmony_ci{
259862306a36Sopenharmony_ci 	unsigned long flags;
259962306a36Sopenharmony_ci	unsigned int rbuf_fill_level;
260062306a36Sopenharmony_ci	DBGINFO(("%s rx_enable(%08x)\n", info->device_name, enable));
260162306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
260262306a36Sopenharmony_ci	/*
260362306a36Sopenharmony_ci	 * enable[31..16] = receive DMA buffer fill level
260462306a36Sopenharmony_ci	 * 0 = noop (leave fill level unchanged)
260562306a36Sopenharmony_ci	 * fill level must be multiple of 4 and <= buffer size
260662306a36Sopenharmony_ci	 */
260762306a36Sopenharmony_ci	rbuf_fill_level = ((unsigned int)enable) >> 16;
260862306a36Sopenharmony_ci	if (rbuf_fill_level) {
260962306a36Sopenharmony_ci		if ((rbuf_fill_level > DMABUFSIZE) || (rbuf_fill_level % 4)) {
261062306a36Sopenharmony_ci			spin_unlock_irqrestore(&info->lock, flags);
261162306a36Sopenharmony_ci			return -EINVAL;
261262306a36Sopenharmony_ci		}
261362306a36Sopenharmony_ci		info->rbuf_fill_level = rbuf_fill_level;
261462306a36Sopenharmony_ci		if (rbuf_fill_level < 128)
261562306a36Sopenharmony_ci			info->rx_pio = 1; /* PIO mode */
261662306a36Sopenharmony_ci		else
261762306a36Sopenharmony_ci			info->rx_pio = 0; /* DMA mode */
261862306a36Sopenharmony_ci		rx_stop(info); /* restart receiver to use new fill level */
261962306a36Sopenharmony_ci	}
262062306a36Sopenharmony_ci
262162306a36Sopenharmony_ci	/*
262262306a36Sopenharmony_ci	 * enable[1..0] = receiver enable command
262362306a36Sopenharmony_ci	 * 0 = disable
262462306a36Sopenharmony_ci	 * 1 = enable
262562306a36Sopenharmony_ci	 * 2 = enable or force hunt mode if already enabled
262662306a36Sopenharmony_ci	 */
262762306a36Sopenharmony_ci	enable &= 3;
262862306a36Sopenharmony_ci	if (enable) {
262962306a36Sopenharmony_ci		if (!info->rx_enabled)
263062306a36Sopenharmony_ci			rx_start(info);
263162306a36Sopenharmony_ci		else if (enable == 2) {
263262306a36Sopenharmony_ci			/* force hunt mode (write 1 to RCR[3]) */
263362306a36Sopenharmony_ci			wr_reg16(info, RCR, rd_reg16(info, RCR) | BIT3);
263462306a36Sopenharmony_ci		}
263562306a36Sopenharmony_ci	} else {
263662306a36Sopenharmony_ci		if (info->rx_enabled)
263762306a36Sopenharmony_ci			rx_stop(info);
263862306a36Sopenharmony_ci	}
263962306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
264062306a36Sopenharmony_ci	return 0;
264162306a36Sopenharmony_ci}
264262306a36Sopenharmony_ci
264362306a36Sopenharmony_ci/*
264462306a36Sopenharmony_ci *  wait for specified event to occur
264562306a36Sopenharmony_ci */
264662306a36Sopenharmony_cistatic int wait_mgsl_event(struct slgt_info *info, int __user *mask_ptr)
264762306a36Sopenharmony_ci{
264862306a36Sopenharmony_ci 	unsigned long flags;
264962306a36Sopenharmony_ci	int s;
265062306a36Sopenharmony_ci	int rc=0;
265162306a36Sopenharmony_ci	struct mgsl_icount cprev, cnow;
265262306a36Sopenharmony_ci	int events;
265362306a36Sopenharmony_ci	int mask;
265462306a36Sopenharmony_ci	struct	_input_signal_events oldsigs, newsigs;
265562306a36Sopenharmony_ci	DECLARE_WAITQUEUE(wait, current);
265662306a36Sopenharmony_ci
265762306a36Sopenharmony_ci	if (get_user(mask, mask_ptr))
265862306a36Sopenharmony_ci		return -EFAULT;
265962306a36Sopenharmony_ci
266062306a36Sopenharmony_ci	DBGINFO(("%s wait_mgsl_event(%d)\n", info->device_name, mask));
266162306a36Sopenharmony_ci
266262306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
266362306a36Sopenharmony_ci
266462306a36Sopenharmony_ci	/* return immediately if state matches requested events */
266562306a36Sopenharmony_ci	get_gtsignals(info);
266662306a36Sopenharmony_ci	s = info->signals;
266762306a36Sopenharmony_ci
266862306a36Sopenharmony_ci	events = mask &
266962306a36Sopenharmony_ci		( ((s & SerialSignal_DSR) ? MgslEvent_DsrActive:MgslEvent_DsrInactive) +
267062306a36Sopenharmony_ci 		  ((s & SerialSignal_DCD) ? MgslEvent_DcdActive:MgslEvent_DcdInactive) +
267162306a36Sopenharmony_ci		  ((s & SerialSignal_CTS) ? MgslEvent_CtsActive:MgslEvent_CtsInactive) +
267262306a36Sopenharmony_ci		  ((s & SerialSignal_RI)  ? MgslEvent_RiActive :MgslEvent_RiInactive) );
267362306a36Sopenharmony_ci	if (events) {
267462306a36Sopenharmony_ci		spin_unlock_irqrestore(&info->lock,flags);
267562306a36Sopenharmony_ci		goto exit;
267662306a36Sopenharmony_ci	}
267762306a36Sopenharmony_ci
267862306a36Sopenharmony_ci	/* save current irq counts */
267962306a36Sopenharmony_ci	cprev = info->icount;
268062306a36Sopenharmony_ci	oldsigs = info->input_signal_events;
268162306a36Sopenharmony_ci
268262306a36Sopenharmony_ci	/* enable hunt and idle irqs if needed */
268362306a36Sopenharmony_ci	if (mask & (MgslEvent_ExitHuntMode+MgslEvent_IdleReceived)) {
268462306a36Sopenharmony_ci		unsigned short val = rd_reg16(info, SCR);
268562306a36Sopenharmony_ci		if (!(val & IRQ_RXIDLE))
268662306a36Sopenharmony_ci			wr_reg16(info, SCR, (unsigned short)(val | IRQ_RXIDLE));
268762306a36Sopenharmony_ci	}
268862306a36Sopenharmony_ci
268962306a36Sopenharmony_ci	set_current_state(TASK_INTERRUPTIBLE);
269062306a36Sopenharmony_ci	add_wait_queue(&info->event_wait_q, &wait);
269162306a36Sopenharmony_ci
269262306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
269362306a36Sopenharmony_ci
269462306a36Sopenharmony_ci	for(;;) {
269562306a36Sopenharmony_ci		schedule();
269662306a36Sopenharmony_ci		if (signal_pending(current)) {
269762306a36Sopenharmony_ci			rc = -ERESTARTSYS;
269862306a36Sopenharmony_ci			break;
269962306a36Sopenharmony_ci		}
270062306a36Sopenharmony_ci
270162306a36Sopenharmony_ci		/* get current irq counts */
270262306a36Sopenharmony_ci		spin_lock_irqsave(&info->lock,flags);
270362306a36Sopenharmony_ci		cnow = info->icount;
270462306a36Sopenharmony_ci		newsigs = info->input_signal_events;
270562306a36Sopenharmony_ci		set_current_state(TASK_INTERRUPTIBLE);
270662306a36Sopenharmony_ci		spin_unlock_irqrestore(&info->lock,flags);
270762306a36Sopenharmony_ci
270862306a36Sopenharmony_ci		/* if no change, wait aborted for some reason */
270962306a36Sopenharmony_ci		if (newsigs.dsr_up   == oldsigs.dsr_up   &&
271062306a36Sopenharmony_ci		    newsigs.dsr_down == oldsigs.dsr_down &&
271162306a36Sopenharmony_ci		    newsigs.dcd_up   == oldsigs.dcd_up   &&
271262306a36Sopenharmony_ci		    newsigs.dcd_down == oldsigs.dcd_down &&
271362306a36Sopenharmony_ci		    newsigs.cts_up   == oldsigs.cts_up   &&
271462306a36Sopenharmony_ci		    newsigs.cts_down == oldsigs.cts_down &&
271562306a36Sopenharmony_ci		    newsigs.ri_up    == oldsigs.ri_up    &&
271662306a36Sopenharmony_ci		    newsigs.ri_down  == oldsigs.ri_down  &&
271762306a36Sopenharmony_ci		    cnow.exithunt    == cprev.exithunt   &&
271862306a36Sopenharmony_ci		    cnow.rxidle      == cprev.rxidle) {
271962306a36Sopenharmony_ci			rc = -EIO;
272062306a36Sopenharmony_ci			break;
272162306a36Sopenharmony_ci		}
272262306a36Sopenharmony_ci
272362306a36Sopenharmony_ci		events = mask &
272462306a36Sopenharmony_ci			( (newsigs.dsr_up   != oldsigs.dsr_up   ? MgslEvent_DsrActive:0)   +
272562306a36Sopenharmony_ci			  (newsigs.dsr_down != oldsigs.dsr_down ? MgslEvent_DsrInactive:0) +
272662306a36Sopenharmony_ci			  (newsigs.dcd_up   != oldsigs.dcd_up   ? MgslEvent_DcdActive:0)   +
272762306a36Sopenharmony_ci			  (newsigs.dcd_down != oldsigs.dcd_down ? MgslEvent_DcdInactive:0) +
272862306a36Sopenharmony_ci			  (newsigs.cts_up   != oldsigs.cts_up   ? MgslEvent_CtsActive:0)   +
272962306a36Sopenharmony_ci			  (newsigs.cts_down != oldsigs.cts_down ? MgslEvent_CtsInactive:0) +
273062306a36Sopenharmony_ci			  (newsigs.ri_up    != oldsigs.ri_up    ? MgslEvent_RiActive:0)    +
273162306a36Sopenharmony_ci			  (newsigs.ri_down  != oldsigs.ri_down  ? MgslEvent_RiInactive:0)  +
273262306a36Sopenharmony_ci			  (cnow.exithunt    != cprev.exithunt   ? MgslEvent_ExitHuntMode:0) +
273362306a36Sopenharmony_ci			  (cnow.rxidle      != cprev.rxidle     ? MgslEvent_IdleReceived:0) );
273462306a36Sopenharmony_ci		if (events)
273562306a36Sopenharmony_ci			break;
273662306a36Sopenharmony_ci
273762306a36Sopenharmony_ci		cprev = cnow;
273862306a36Sopenharmony_ci		oldsigs = newsigs;
273962306a36Sopenharmony_ci	}
274062306a36Sopenharmony_ci
274162306a36Sopenharmony_ci	remove_wait_queue(&info->event_wait_q, &wait);
274262306a36Sopenharmony_ci	set_current_state(TASK_RUNNING);
274362306a36Sopenharmony_ci
274462306a36Sopenharmony_ci
274562306a36Sopenharmony_ci	if (mask & (MgslEvent_ExitHuntMode + MgslEvent_IdleReceived)) {
274662306a36Sopenharmony_ci		spin_lock_irqsave(&info->lock,flags);
274762306a36Sopenharmony_ci		if (!waitqueue_active(&info->event_wait_q)) {
274862306a36Sopenharmony_ci			/* disable enable exit hunt mode/idle rcvd IRQs */
274962306a36Sopenharmony_ci			wr_reg16(info, SCR,
275062306a36Sopenharmony_ci				(unsigned short)(rd_reg16(info, SCR) & ~IRQ_RXIDLE));
275162306a36Sopenharmony_ci		}
275262306a36Sopenharmony_ci		spin_unlock_irqrestore(&info->lock,flags);
275362306a36Sopenharmony_ci	}
275462306a36Sopenharmony_ciexit:
275562306a36Sopenharmony_ci	if (rc == 0)
275662306a36Sopenharmony_ci		rc = put_user(events, mask_ptr);
275762306a36Sopenharmony_ci	return rc;
275862306a36Sopenharmony_ci}
275962306a36Sopenharmony_ci
276062306a36Sopenharmony_cistatic int get_interface(struct slgt_info *info, int __user *if_mode)
276162306a36Sopenharmony_ci{
276262306a36Sopenharmony_ci	DBGINFO(("%s get_interface=%x\n", info->device_name, info->if_mode));
276362306a36Sopenharmony_ci	if (put_user(info->if_mode, if_mode))
276462306a36Sopenharmony_ci		return -EFAULT;
276562306a36Sopenharmony_ci	return 0;
276662306a36Sopenharmony_ci}
276762306a36Sopenharmony_ci
276862306a36Sopenharmony_cistatic int set_interface(struct slgt_info *info, int if_mode)
276962306a36Sopenharmony_ci{
277062306a36Sopenharmony_ci 	unsigned long flags;
277162306a36Sopenharmony_ci	unsigned short val;
277262306a36Sopenharmony_ci
277362306a36Sopenharmony_ci	DBGINFO(("%s set_interface=%x)\n", info->device_name, if_mode));
277462306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
277562306a36Sopenharmony_ci	info->if_mode = if_mode;
277662306a36Sopenharmony_ci
277762306a36Sopenharmony_ci	msc_set_vcr(info);
277862306a36Sopenharmony_ci
277962306a36Sopenharmony_ci	/* TCR (tx control) 07  1=RTS driver control */
278062306a36Sopenharmony_ci	val = rd_reg16(info, TCR);
278162306a36Sopenharmony_ci	if (info->if_mode & MGSL_INTERFACE_RTS_EN)
278262306a36Sopenharmony_ci		val |= BIT7;
278362306a36Sopenharmony_ci	else
278462306a36Sopenharmony_ci		val &= ~BIT7;
278562306a36Sopenharmony_ci	wr_reg16(info, TCR, val);
278662306a36Sopenharmony_ci
278762306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
278862306a36Sopenharmony_ci	return 0;
278962306a36Sopenharmony_ci}
279062306a36Sopenharmony_ci
279162306a36Sopenharmony_cistatic int get_xsync(struct slgt_info *info, int __user *xsync)
279262306a36Sopenharmony_ci{
279362306a36Sopenharmony_ci	DBGINFO(("%s get_xsync=%x\n", info->device_name, info->xsync));
279462306a36Sopenharmony_ci	if (put_user(info->xsync, xsync))
279562306a36Sopenharmony_ci		return -EFAULT;
279662306a36Sopenharmony_ci	return 0;
279762306a36Sopenharmony_ci}
279862306a36Sopenharmony_ci
279962306a36Sopenharmony_ci/*
280062306a36Sopenharmony_ci * set extended sync pattern (1 to 4 bytes) for extended sync mode
280162306a36Sopenharmony_ci *
280262306a36Sopenharmony_ci * sync pattern is contained in least significant bytes of value
280362306a36Sopenharmony_ci * most significant byte of sync pattern is oldest (1st sent/detected)
280462306a36Sopenharmony_ci */
280562306a36Sopenharmony_cistatic int set_xsync(struct slgt_info *info, int xsync)
280662306a36Sopenharmony_ci{
280762306a36Sopenharmony_ci	unsigned long flags;
280862306a36Sopenharmony_ci
280962306a36Sopenharmony_ci	DBGINFO(("%s set_xsync=%x)\n", info->device_name, xsync));
281062306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock, flags);
281162306a36Sopenharmony_ci	info->xsync = xsync;
281262306a36Sopenharmony_ci	wr_reg32(info, XSR, xsync);
281362306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->lock, flags);
281462306a36Sopenharmony_ci	return 0;
281562306a36Sopenharmony_ci}
281662306a36Sopenharmony_ci
281762306a36Sopenharmony_cistatic int get_xctrl(struct slgt_info *info, int __user *xctrl)
281862306a36Sopenharmony_ci{
281962306a36Sopenharmony_ci	DBGINFO(("%s get_xctrl=%x\n", info->device_name, info->xctrl));
282062306a36Sopenharmony_ci	if (put_user(info->xctrl, xctrl))
282162306a36Sopenharmony_ci		return -EFAULT;
282262306a36Sopenharmony_ci	return 0;
282362306a36Sopenharmony_ci}
282462306a36Sopenharmony_ci
282562306a36Sopenharmony_ci/*
282662306a36Sopenharmony_ci * set extended control options
282762306a36Sopenharmony_ci *
282862306a36Sopenharmony_ci * xctrl[31:19] reserved, must be zero
282962306a36Sopenharmony_ci * xctrl[18:17] extended sync pattern length in bytes
283062306a36Sopenharmony_ci *              00 = 1 byte  in xsr[7:0]
283162306a36Sopenharmony_ci *              01 = 2 bytes in xsr[15:0]
283262306a36Sopenharmony_ci *              10 = 3 bytes in xsr[23:0]
283362306a36Sopenharmony_ci *              11 = 4 bytes in xsr[31:0]
283462306a36Sopenharmony_ci * xctrl[16]    1 = enable terminal count, 0=disabled
283562306a36Sopenharmony_ci * xctrl[15:0]  receive terminal count for fixed length packets
283662306a36Sopenharmony_ci *              value is count minus one (0 = 1 byte packet)
283762306a36Sopenharmony_ci *              when terminal count is reached, receiver
283862306a36Sopenharmony_ci *              automatically returns to hunt mode and receive
283962306a36Sopenharmony_ci *              FIFO contents are flushed to DMA buffers with
284062306a36Sopenharmony_ci *              end of frame (EOF) status
284162306a36Sopenharmony_ci */
284262306a36Sopenharmony_cistatic int set_xctrl(struct slgt_info *info, int xctrl)
284362306a36Sopenharmony_ci{
284462306a36Sopenharmony_ci	unsigned long flags;
284562306a36Sopenharmony_ci
284662306a36Sopenharmony_ci	DBGINFO(("%s set_xctrl=%x)\n", info->device_name, xctrl));
284762306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock, flags);
284862306a36Sopenharmony_ci	info->xctrl = xctrl;
284962306a36Sopenharmony_ci	wr_reg32(info, XCR, xctrl);
285062306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->lock, flags);
285162306a36Sopenharmony_ci	return 0;
285262306a36Sopenharmony_ci}
285362306a36Sopenharmony_ci
285462306a36Sopenharmony_ci/*
285562306a36Sopenharmony_ci * set general purpose IO pin state and direction
285662306a36Sopenharmony_ci *
285762306a36Sopenharmony_ci * user_gpio fields:
285862306a36Sopenharmony_ci * state   each bit indicates a pin state
285962306a36Sopenharmony_ci * smask   set bit indicates pin state to set
286062306a36Sopenharmony_ci * dir     each bit indicates a pin direction (0=input, 1=output)
286162306a36Sopenharmony_ci * dmask   set bit indicates pin direction to set
286262306a36Sopenharmony_ci */
286362306a36Sopenharmony_cistatic int set_gpio(struct slgt_info *info, struct gpio_desc __user *user_gpio)
286462306a36Sopenharmony_ci{
286562306a36Sopenharmony_ci 	unsigned long flags;
286662306a36Sopenharmony_ci	struct gpio_desc gpio;
286762306a36Sopenharmony_ci	__u32 data;
286862306a36Sopenharmony_ci
286962306a36Sopenharmony_ci	if (!info->gpio_present)
287062306a36Sopenharmony_ci		return -EINVAL;
287162306a36Sopenharmony_ci	if (copy_from_user(&gpio, user_gpio, sizeof(gpio)))
287262306a36Sopenharmony_ci		return -EFAULT;
287362306a36Sopenharmony_ci	DBGINFO(("%s set_gpio state=%08x smask=%08x dir=%08x dmask=%08x\n",
287462306a36Sopenharmony_ci		 info->device_name, gpio.state, gpio.smask,
287562306a36Sopenharmony_ci		 gpio.dir, gpio.dmask));
287662306a36Sopenharmony_ci
287762306a36Sopenharmony_ci	spin_lock_irqsave(&info->port_array[0]->lock, flags);
287862306a36Sopenharmony_ci	if (gpio.dmask) {
287962306a36Sopenharmony_ci		data = rd_reg32(info, IODR);
288062306a36Sopenharmony_ci		data |= gpio.dmask & gpio.dir;
288162306a36Sopenharmony_ci		data &= ~(gpio.dmask & ~gpio.dir);
288262306a36Sopenharmony_ci		wr_reg32(info, IODR, data);
288362306a36Sopenharmony_ci	}
288462306a36Sopenharmony_ci	if (gpio.smask) {
288562306a36Sopenharmony_ci		data = rd_reg32(info, IOVR);
288662306a36Sopenharmony_ci		data |= gpio.smask & gpio.state;
288762306a36Sopenharmony_ci		data &= ~(gpio.smask & ~gpio.state);
288862306a36Sopenharmony_ci		wr_reg32(info, IOVR, data);
288962306a36Sopenharmony_ci	}
289062306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->port_array[0]->lock, flags);
289162306a36Sopenharmony_ci
289262306a36Sopenharmony_ci	return 0;
289362306a36Sopenharmony_ci}
289462306a36Sopenharmony_ci
289562306a36Sopenharmony_ci/*
289662306a36Sopenharmony_ci * get general purpose IO pin state and direction
289762306a36Sopenharmony_ci */
289862306a36Sopenharmony_cistatic int get_gpio(struct slgt_info *info, struct gpio_desc __user *user_gpio)
289962306a36Sopenharmony_ci{
290062306a36Sopenharmony_ci	struct gpio_desc gpio;
290162306a36Sopenharmony_ci	if (!info->gpio_present)
290262306a36Sopenharmony_ci		return -EINVAL;
290362306a36Sopenharmony_ci	gpio.state = rd_reg32(info, IOVR);
290462306a36Sopenharmony_ci	gpio.smask = 0xffffffff;
290562306a36Sopenharmony_ci	gpio.dir   = rd_reg32(info, IODR);
290662306a36Sopenharmony_ci	gpio.dmask = 0xffffffff;
290762306a36Sopenharmony_ci	if (copy_to_user(user_gpio, &gpio, sizeof(gpio)))
290862306a36Sopenharmony_ci		return -EFAULT;
290962306a36Sopenharmony_ci	DBGINFO(("%s get_gpio state=%08x dir=%08x\n",
291062306a36Sopenharmony_ci		 info->device_name, gpio.state, gpio.dir));
291162306a36Sopenharmony_ci	return 0;
291262306a36Sopenharmony_ci}
291362306a36Sopenharmony_ci
291462306a36Sopenharmony_ci/*
291562306a36Sopenharmony_ci * conditional wait facility
291662306a36Sopenharmony_ci */
291762306a36Sopenharmony_cistatic void init_cond_wait(struct cond_wait *w, unsigned int data)
291862306a36Sopenharmony_ci{
291962306a36Sopenharmony_ci	init_waitqueue_head(&w->q);
292062306a36Sopenharmony_ci	init_waitqueue_entry(&w->wait, current);
292162306a36Sopenharmony_ci	w->data = data;
292262306a36Sopenharmony_ci}
292362306a36Sopenharmony_ci
292462306a36Sopenharmony_cistatic void add_cond_wait(struct cond_wait **head, struct cond_wait *w)
292562306a36Sopenharmony_ci{
292662306a36Sopenharmony_ci	set_current_state(TASK_INTERRUPTIBLE);
292762306a36Sopenharmony_ci	add_wait_queue(&w->q, &w->wait);
292862306a36Sopenharmony_ci	w->next = *head;
292962306a36Sopenharmony_ci	*head = w;
293062306a36Sopenharmony_ci}
293162306a36Sopenharmony_ci
293262306a36Sopenharmony_cistatic void remove_cond_wait(struct cond_wait **head, struct cond_wait *cw)
293362306a36Sopenharmony_ci{
293462306a36Sopenharmony_ci	struct cond_wait *w, *prev;
293562306a36Sopenharmony_ci	remove_wait_queue(&cw->q, &cw->wait);
293662306a36Sopenharmony_ci	set_current_state(TASK_RUNNING);
293762306a36Sopenharmony_ci	for (w = *head, prev = NULL ; w != NULL ; prev = w, w = w->next) {
293862306a36Sopenharmony_ci		if (w == cw) {
293962306a36Sopenharmony_ci			if (prev != NULL)
294062306a36Sopenharmony_ci				prev->next = w->next;
294162306a36Sopenharmony_ci			else
294262306a36Sopenharmony_ci				*head = w->next;
294362306a36Sopenharmony_ci			break;
294462306a36Sopenharmony_ci		}
294562306a36Sopenharmony_ci	}
294662306a36Sopenharmony_ci}
294762306a36Sopenharmony_ci
294862306a36Sopenharmony_cistatic void flush_cond_wait(struct cond_wait **head)
294962306a36Sopenharmony_ci{
295062306a36Sopenharmony_ci	while (*head != NULL) {
295162306a36Sopenharmony_ci		wake_up_interruptible(&(*head)->q);
295262306a36Sopenharmony_ci		*head = (*head)->next;
295362306a36Sopenharmony_ci	}
295462306a36Sopenharmony_ci}
295562306a36Sopenharmony_ci
295662306a36Sopenharmony_ci/*
295762306a36Sopenharmony_ci * wait for general purpose I/O pin(s) to enter specified state
295862306a36Sopenharmony_ci *
295962306a36Sopenharmony_ci * user_gpio fields:
296062306a36Sopenharmony_ci * state - bit indicates target pin state
296162306a36Sopenharmony_ci * smask - set bit indicates watched pin
296262306a36Sopenharmony_ci *
296362306a36Sopenharmony_ci * The wait ends when at least one watched pin enters the specified
296462306a36Sopenharmony_ci * state. When 0 (no error) is returned, user_gpio->state is set to the
296562306a36Sopenharmony_ci * state of all GPIO pins when the wait ends.
296662306a36Sopenharmony_ci *
296762306a36Sopenharmony_ci * Note: Each pin may be a dedicated input, dedicated output, or
296862306a36Sopenharmony_ci * configurable input/output. The number and configuration of pins
296962306a36Sopenharmony_ci * varies with the specific adapter model. Only input pins (dedicated
297062306a36Sopenharmony_ci * or configured) can be monitored with this function.
297162306a36Sopenharmony_ci */
297262306a36Sopenharmony_cistatic int wait_gpio(struct slgt_info *info, struct gpio_desc __user *user_gpio)
297362306a36Sopenharmony_ci{
297462306a36Sopenharmony_ci 	unsigned long flags;
297562306a36Sopenharmony_ci	int rc = 0;
297662306a36Sopenharmony_ci	struct gpio_desc gpio;
297762306a36Sopenharmony_ci	struct cond_wait wait;
297862306a36Sopenharmony_ci	u32 state;
297962306a36Sopenharmony_ci
298062306a36Sopenharmony_ci	if (!info->gpio_present)
298162306a36Sopenharmony_ci		return -EINVAL;
298262306a36Sopenharmony_ci	if (copy_from_user(&gpio, user_gpio, sizeof(gpio)))
298362306a36Sopenharmony_ci		return -EFAULT;
298462306a36Sopenharmony_ci	DBGINFO(("%s wait_gpio() state=%08x smask=%08x\n",
298562306a36Sopenharmony_ci		 info->device_name, gpio.state, gpio.smask));
298662306a36Sopenharmony_ci	/* ignore output pins identified by set IODR bit */
298762306a36Sopenharmony_ci	if ((gpio.smask &= ~rd_reg32(info, IODR)) == 0)
298862306a36Sopenharmony_ci		return -EINVAL;
298962306a36Sopenharmony_ci	init_cond_wait(&wait, gpio.smask);
299062306a36Sopenharmony_ci
299162306a36Sopenharmony_ci	spin_lock_irqsave(&info->port_array[0]->lock, flags);
299262306a36Sopenharmony_ci	/* enable interrupts for watched pins */
299362306a36Sopenharmony_ci	wr_reg32(info, IOER, rd_reg32(info, IOER) | gpio.smask);
299462306a36Sopenharmony_ci	/* get current pin states */
299562306a36Sopenharmony_ci	state = rd_reg32(info, IOVR);
299662306a36Sopenharmony_ci
299762306a36Sopenharmony_ci	if (gpio.smask & ~(state ^ gpio.state)) {
299862306a36Sopenharmony_ci		/* already in target state */
299962306a36Sopenharmony_ci		gpio.state = state;
300062306a36Sopenharmony_ci	} else {
300162306a36Sopenharmony_ci		/* wait for target state */
300262306a36Sopenharmony_ci		add_cond_wait(&info->gpio_wait_q, &wait);
300362306a36Sopenharmony_ci		spin_unlock_irqrestore(&info->port_array[0]->lock, flags);
300462306a36Sopenharmony_ci		schedule();
300562306a36Sopenharmony_ci		if (signal_pending(current))
300662306a36Sopenharmony_ci			rc = -ERESTARTSYS;
300762306a36Sopenharmony_ci		else
300862306a36Sopenharmony_ci			gpio.state = wait.data;
300962306a36Sopenharmony_ci		spin_lock_irqsave(&info->port_array[0]->lock, flags);
301062306a36Sopenharmony_ci		remove_cond_wait(&info->gpio_wait_q, &wait);
301162306a36Sopenharmony_ci	}
301262306a36Sopenharmony_ci
301362306a36Sopenharmony_ci	/* disable all GPIO interrupts if no waiting processes */
301462306a36Sopenharmony_ci	if (info->gpio_wait_q == NULL)
301562306a36Sopenharmony_ci		wr_reg32(info, IOER, 0);
301662306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->port_array[0]->lock, flags);
301762306a36Sopenharmony_ci
301862306a36Sopenharmony_ci	if ((rc == 0) && copy_to_user(user_gpio, &gpio, sizeof(gpio)))
301962306a36Sopenharmony_ci		rc = -EFAULT;
302062306a36Sopenharmony_ci	return rc;
302162306a36Sopenharmony_ci}
302262306a36Sopenharmony_ci
302362306a36Sopenharmony_cistatic int modem_input_wait(struct slgt_info *info,int arg)
302462306a36Sopenharmony_ci{
302562306a36Sopenharmony_ci 	unsigned long flags;
302662306a36Sopenharmony_ci	int rc;
302762306a36Sopenharmony_ci	struct mgsl_icount cprev, cnow;
302862306a36Sopenharmony_ci	DECLARE_WAITQUEUE(wait, current);
302962306a36Sopenharmony_ci
303062306a36Sopenharmony_ci	/* save current irq counts */
303162306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
303262306a36Sopenharmony_ci	cprev = info->icount;
303362306a36Sopenharmony_ci	add_wait_queue(&info->status_event_wait_q, &wait);
303462306a36Sopenharmony_ci	set_current_state(TASK_INTERRUPTIBLE);
303562306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
303662306a36Sopenharmony_ci
303762306a36Sopenharmony_ci	for(;;) {
303862306a36Sopenharmony_ci		schedule();
303962306a36Sopenharmony_ci		if (signal_pending(current)) {
304062306a36Sopenharmony_ci			rc = -ERESTARTSYS;
304162306a36Sopenharmony_ci			break;
304262306a36Sopenharmony_ci		}
304362306a36Sopenharmony_ci
304462306a36Sopenharmony_ci		/* get new irq counts */
304562306a36Sopenharmony_ci		spin_lock_irqsave(&info->lock,flags);
304662306a36Sopenharmony_ci		cnow = info->icount;
304762306a36Sopenharmony_ci		set_current_state(TASK_INTERRUPTIBLE);
304862306a36Sopenharmony_ci		spin_unlock_irqrestore(&info->lock,flags);
304962306a36Sopenharmony_ci
305062306a36Sopenharmony_ci		/* if no change, wait aborted for some reason */
305162306a36Sopenharmony_ci		if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
305262306a36Sopenharmony_ci		    cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) {
305362306a36Sopenharmony_ci			rc = -EIO;
305462306a36Sopenharmony_ci			break;
305562306a36Sopenharmony_ci		}
305662306a36Sopenharmony_ci
305762306a36Sopenharmony_ci		/* check for change in caller specified modem input */
305862306a36Sopenharmony_ci		if ((arg & TIOCM_RNG && cnow.rng != cprev.rng) ||
305962306a36Sopenharmony_ci		    (arg & TIOCM_DSR && cnow.dsr != cprev.dsr) ||
306062306a36Sopenharmony_ci		    (arg & TIOCM_CD  && cnow.dcd != cprev.dcd) ||
306162306a36Sopenharmony_ci		    (arg & TIOCM_CTS && cnow.cts != cprev.cts)) {
306262306a36Sopenharmony_ci			rc = 0;
306362306a36Sopenharmony_ci			break;
306462306a36Sopenharmony_ci		}
306562306a36Sopenharmony_ci
306662306a36Sopenharmony_ci		cprev = cnow;
306762306a36Sopenharmony_ci	}
306862306a36Sopenharmony_ci	remove_wait_queue(&info->status_event_wait_q, &wait);
306962306a36Sopenharmony_ci	set_current_state(TASK_RUNNING);
307062306a36Sopenharmony_ci	return rc;
307162306a36Sopenharmony_ci}
307262306a36Sopenharmony_ci
307362306a36Sopenharmony_ci/*
307462306a36Sopenharmony_ci *  return state of serial control and status signals
307562306a36Sopenharmony_ci */
307662306a36Sopenharmony_cistatic int tiocmget(struct tty_struct *tty)
307762306a36Sopenharmony_ci{
307862306a36Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
307962306a36Sopenharmony_ci	unsigned int result;
308062306a36Sopenharmony_ci 	unsigned long flags;
308162306a36Sopenharmony_ci
308262306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
308362306a36Sopenharmony_ci 	get_gtsignals(info);
308462306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
308562306a36Sopenharmony_ci
308662306a36Sopenharmony_ci	result = ((info->signals & SerialSignal_RTS) ? TIOCM_RTS:0) +
308762306a36Sopenharmony_ci		((info->signals & SerialSignal_DTR) ? TIOCM_DTR:0) +
308862306a36Sopenharmony_ci		((info->signals & SerialSignal_DCD) ? TIOCM_CAR:0) +
308962306a36Sopenharmony_ci		((info->signals & SerialSignal_RI)  ? TIOCM_RNG:0) +
309062306a36Sopenharmony_ci		((info->signals & SerialSignal_DSR) ? TIOCM_DSR:0) +
309162306a36Sopenharmony_ci		((info->signals & SerialSignal_CTS) ? TIOCM_CTS:0);
309262306a36Sopenharmony_ci
309362306a36Sopenharmony_ci	DBGINFO(("%s tiocmget value=%08X\n", info->device_name, result));
309462306a36Sopenharmony_ci	return result;
309562306a36Sopenharmony_ci}
309662306a36Sopenharmony_ci
309762306a36Sopenharmony_ci/*
309862306a36Sopenharmony_ci * set modem control signals (DTR/RTS)
309962306a36Sopenharmony_ci *
310062306a36Sopenharmony_ci * 	cmd	signal command: TIOCMBIS = set bit TIOCMBIC = clear bit
310162306a36Sopenharmony_ci *		TIOCMSET = set/clear signal values
310262306a36Sopenharmony_ci * 	value	bit mask for command
310362306a36Sopenharmony_ci */
310462306a36Sopenharmony_cistatic int tiocmset(struct tty_struct *tty,
310562306a36Sopenharmony_ci		    unsigned int set, unsigned int clear)
310662306a36Sopenharmony_ci{
310762306a36Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
310862306a36Sopenharmony_ci 	unsigned long flags;
310962306a36Sopenharmony_ci
311062306a36Sopenharmony_ci	DBGINFO(("%s tiocmset(%x,%x)\n", info->device_name, set, clear));
311162306a36Sopenharmony_ci
311262306a36Sopenharmony_ci	if (set & TIOCM_RTS)
311362306a36Sopenharmony_ci		info->signals |= SerialSignal_RTS;
311462306a36Sopenharmony_ci	if (set & TIOCM_DTR)
311562306a36Sopenharmony_ci		info->signals |= SerialSignal_DTR;
311662306a36Sopenharmony_ci	if (clear & TIOCM_RTS)
311762306a36Sopenharmony_ci		info->signals &= ~SerialSignal_RTS;
311862306a36Sopenharmony_ci	if (clear & TIOCM_DTR)
311962306a36Sopenharmony_ci		info->signals &= ~SerialSignal_DTR;
312062306a36Sopenharmony_ci
312162306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
312262306a36Sopenharmony_ci	set_gtsignals(info);
312362306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
312462306a36Sopenharmony_ci	return 0;
312562306a36Sopenharmony_ci}
312662306a36Sopenharmony_ci
312762306a36Sopenharmony_cistatic bool carrier_raised(struct tty_port *port)
312862306a36Sopenharmony_ci{
312962306a36Sopenharmony_ci	unsigned long flags;
313062306a36Sopenharmony_ci	struct slgt_info *info = container_of(port, struct slgt_info, port);
313162306a36Sopenharmony_ci
313262306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
313362306a36Sopenharmony_ci	get_gtsignals(info);
313462306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
313562306a36Sopenharmony_ci
313662306a36Sopenharmony_ci	return info->signals & SerialSignal_DCD;
313762306a36Sopenharmony_ci}
313862306a36Sopenharmony_ci
313962306a36Sopenharmony_cistatic void dtr_rts(struct tty_port *port, bool active)
314062306a36Sopenharmony_ci{
314162306a36Sopenharmony_ci	unsigned long flags;
314262306a36Sopenharmony_ci	struct slgt_info *info = container_of(port, struct slgt_info, port);
314362306a36Sopenharmony_ci
314462306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
314562306a36Sopenharmony_ci	if (active)
314662306a36Sopenharmony_ci		info->signals |= SerialSignal_RTS | SerialSignal_DTR;
314762306a36Sopenharmony_ci	else
314862306a36Sopenharmony_ci		info->signals &= ~(SerialSignal_RTS | SerialSignal_DTR);
314962306a36Sopenharmony_ci	set_gtsignals(info);
315062306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
315162306a36Sopenharmony_ci}
315262306a36Sopenharmony_ci
315362306a36Sopenharmony_ci
315462306a36Sopenharmony_ci/*
315562306a36Sopenharmony_ci *  block current process until the device is ready to open
315662306a36Sopenharmony_ci */
315762306a36Sopenharmony_cistatic int block_til_ready(struct tty_struct *tty, struct file *filp,
315862306a36Sopenharmony_ci			   struct slgt_info *info)
315962306a36Sopenharmony_ci{
316062306a36Sopenharmony_ci	DECLARE_WAITQUEUE(wait, current);
316162306a36Sopenharmony_ci	int		retval;
316262306a36Sopenharmony_ci	bool		do_clocal = false;
316362306a36Sopenharmony_ci	unsigned long	flags;
316462306a36Sopenharmony_ci	bool		cd;
316562306a36Sopenharmony_ci	struct tty_port *port = &info->port;
316662306a36Sopenharmony_ci
316762306a36Sopenharmony_ci	DBGINFO(("%s block_til_ready\n", tty->driver->name));
316862306a36Sopenharmony_ci
316962306a36Sopenharmony_ci	if (filp->f_flags & O_NONBLOCK || tty_io_error(tty)) {
317062306a36Sopenharmony_ci		/* nonblock mode is set or port is not enabled */
317162306a36Sopenharmony_ci		tty_port_set_active(port, true);
317262306a36Sopenharmony_ci		return 0;
317362306a36Sopenharmony_ci	}
317462306a36Sopenharmony_ci
317562306a36Sopenharmony_ci	if (C_CLOCAL(tty))
317662306a36Sopenharmony_ci		do_clocal = true;
317762306a36Sopenharmony_ci
317862306a36Sopenharmony_ci	/* Wait for carrier detect and the line to become
317962306a36Sopenharmony_ci	 * free (i.e., not in use by the callout).  While we are in
318062306a36Sopenharmony_ci	 * this loop, port->count is dropped by one, so that
318162306a36Sopenharmony_ci	 * close() knows when to free things.  We restore it upon
318262306a36Sopenharmony_ci	 * exit, either normal or abnormal.
318362306a36Sopenharmony_ci	 */
318462306a36Sopenharmony_ci
318562306a36Sopenharmony_ci	retval = 0;
318662306a36Sopenharmony_ci	add_wait_queue(&port->open_wait, &wait);
318762306a36Sopenharmony_ci
318862306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock, flags);
318962306a36Sopenharmony_ci	port->count--;
319062306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->lock, flags);
319162306a36Sopenharmony_ci	port->blocked_open++;
319262306a36Sopenharmony_ci
319362306a36Sopenharmony_ci	while (1) {
319462306a36Sopenharmony_ci		if (C_BAUD(tty) && tty_port_initialized(port))
319562306a36Sopenharmony_ci			tty_port_raise_dtr_rts(port);
319662306a36Sopenharmony_ci
319762306a36Sopenharmony_ci		set_current_state(TASK_INTERRUPTIBLE);
319862306a36Sopenharmony_ci
319962306a36Sopenharmony_ci		if (tty_hung_up_p(filp) || !tty_port_initialized(port)) {
320062306a36Sopenharmony_ci			retval = (port->flags & ASYNC_HUP_NOTIFY) ?
320162306a36Sopenharmony_ci					-EAGAIN : -ERESTARTSYS;
320262306a36Sopenharmony_ci			break;
320362306a36Sopenharmony_ci		}
320462306a36Sopenharmony_ci
320562306a36Sopenharmony_ci		cd = tty_port_carrier_raised(port);
320662306a36Sopenharmony_ci		if (do_clocal || cd)
320762306a36Sopenharmony_ci			break;
320862306a36Sopenharmony_ci
320962306a36Sopenharmony_ci		if (signal_pending(current)) {
321062306a36Sopenharmony_ci			retval = -ERESTARTSYS;
321162306a36Sopenharmony_ci			break;
321262306a36Sopenharmony_ci		}
321362306a36Sopenharmony_ci
321462306a36Sopenharmony_ci		DBGINFO(("%s block_til_ready wait\n", tty->driver->name));
321562306a36Sopenharmony_ci		tty_unlock(tty);
321662306a36Sopenharmony_ci		schedule();
321762306a36Sopenharmony_ci		tty_lock(tty);
321862306a36Sopenharmony_ci	}
321962306a36Sopenharmony_ci
322062306a36Sopenharmony_ci	set_current_state(TASK_RUNNING);
322162306a36Sopenharmony_ci	remove_wait_queue(&port->open_wait, &wait);
322262306a36Sopenharmony_ci
322362306a36Sopenharmony_ci	if (!tty_hung_up_p(filp))
322462306a36Sopenharmony_ci		port->count++;
322562306a36Sopenharmony_ci	port->blocked_open--;
322662306a36Sopenharmony_ci
322762306a36Sopenharmony_ci	if (!retval)
322862306a36Sopenharmony_ci		tty_port_set_active(port, true);
322962306a36Sopenharmony_ci
323062306a36Sopenharmony_ci	DBGINFO(("%s block_til_ready ready, rc=%d\n", tty->driver->name, retval));
323162306a36Sopenharmony_ci	return retval;
323262306a36Sopenharmony_ci}
323362306a36Sopenharmony_ci
323462306a36Sopenharmony_ci/*
323562306a36Sopenharmony_ci * allocate buffers used for calling line discipline receive_buf
323662306a36Sopenharmony_ci * directly in synchronous mode
323762306a36Sopenharmony_ci * note: add 5 bytes to max frame size to allow appending
323862306a36Sopenharmony_ci * 32-bit CRC and status byte when configured to do so
323962306a36Sopenharmony_ci */
324062306a36Sopenharmony_cistatic int alloc_tmp_rbuf(struct slgt_info *info)
324162306a36Sopenharmony_ci{
324262306a36Sopenharmony_ci	info->tmp_rbuf = kmalloc(info->max_frame_size + 5, GFP_KERNEL);
324362306a36Sopenharmony_ci	if (info->tmp_rbuf == NULL)
324462306a36Sopenharmony_ci		return -ENOMEM;
324562306a36Sopenharmony_ci
324662306a36Sopenharmony_ci	return 0;
324762306a36Sopenharmony_ci}
324862306a36Sopenharmony_ci
324962306a36Sopenharmony_cistatic void free_tmp_rbuf(struct slgt_info *info)
325062306a36Sopenharmony_ci{
325162306a36Sopenharmony_ci	kfree(info->tmp_rbuf);
325262306a36Sopenharmony_ci	info->tmp_rbuf = NULL;
325362306a36Sopenharmony_ci}
325462306a36Sopenharmony_ci
325562306a36Sopenharmony_ci/*
325662306a36Sopenharmony_ci * allocate DMA descriptor lists.
325762306a36Sopenharmony_ci */
325862306a36Sopenharmony_cistatic int alloc_desc(struct slgt_info *info)
325962306a36Sopenharmony_ci{
326062306a36Sopenharmony_ci	unsigned int i;
326162306a36Sopenharmony_ci	unsigned int pbufs;
326262306a36Sopenharmony_ci
326362306a36Sopenharmony_ci	/* allocate memory to hold descriptor lists */
326462306a36Sopenharmony_ci	info->bufs = dma_alloc_coherent(&info->pdev->dev, DESC_LIST_SIZE,
326562306a36Sopenharmony_ci					&info->bufs_dma_addr, GFP_KERNEL);
326662306a36Sopenharmony_ci	if (info->bufs == NULL)
326762306a36Sopenharmony_ci		return -ENOMEM;
326862306a36Sopenharmony_ci
326962306a36Sopenharmony_ci	info->rbufs = (struct slgt_desc*)info->bufs;
327062306a36Sopenharmony_ci	info->tbufs = ((struct slgt_desc*)info->bufs) + info->rbuf_count;
327162306a36Sopenharmony_ci
327262306a36Sopenharmony_ci	pbufs = (unsigned int)info->bufs_dma_addr;
327362306a36Sopenharmony_ci
327462306a36Sopenharmony_ci	/*
327562306a36Sopenharmony_ci	 * Build circular lists of descriptors
327662306a36Sopenharmony_ci	 */
327762306a36Sopenharmony_ci
327862306a36Sopenharmony_ci	for (i=0; i < info->rbuf_count; i++) {
327962306a36Sopenharmony_ci		/* physical address of this descriptor */
328062306a36Sopenharmony_ci		info->rbufs[i].pdesc = pbufs + (i * sizeof(struct slgt_desc));
328162306a36Sopenharmony_ci
328262306a36Sopenharmony_ci		/* physical address of next descriptor */
328362306a36Sopenharmony_ci		if (i == info->rbuf_count - 1)
328462306a36Sopenharmony_ci			info->rbufs[i].next = cpu_to_le32(pbufs);
328562306a36Sopenharmony_ci		else
328662306a36Sopenharmony_ci			info->rbufs[i].next = cpu_to_le32(pbufs + ((i+1) * sizeof(struct slgt_desc)));
328762306a36Sopenharmony_ci		set_desc_count(info->rbufs[i], DMABUFSIZE);
328862306a36Sopenharmony_ci	}
328962306a36Sopenharmony_ci
329062306a36Sopenharmony_ci	for (i=0; i < info->tbuf_count; i++) {
329162306a36Sopenharmony_ci		/* physical address of this descriptor */
329262306a36Sopenharmony_ci		info->tbufs[i].pdesc = pbufs + ((info->rbuf_count + i) * sizeof(struct slgt_desc));
329362306a36Sopenharmony_ci
329462306a36Sopenharmony_ci		/* physical address of next descriptor */
329562306a36Sopenharmony_ci		if (i == info->tbuf_count - 1)
329662306a36Sopenharmony_ci			info->tbufs[i].next = cpu_to_le32(pbufs + info->rbuf_count * sizeof(struct slgt_desc));
329762306a36Sopenharmony_ci		else
329862306a36Sopenharmony_ci			info->tbufs[i].next = cpu_to_le32(pbufs + ((info->rbuf_count + i + 1) * sizeof(struct slgt_desc)));
329962306a36Sopenharmony_ci	}
330062306a36Sopenharmony_ci
330162306a36Sopenharmony_ci	return 0;
330262306a36Sopenharmony_ci}
330362306a36Sopenharmony_ci
330462306a36Sopenharmony_cistatic void free_desc(struct slgt_info *info)
330562306a36Sopenharmony_ci{
330662306a36Sopenharmony_ci	if (info->bufs != NULL) {
330762306a36Sopenharmony_ci		dma_free_coherent(&info->pdev->dev, DESC_LIST_SIZE,
330862306a36Sopenharmony_ci				  info->bufs, info->bufs_dma_addr);
330962306a36Sopenharmony_ci		info->bufs  = NULL;
331062306a36Sopenharmony_ci		info->rbufs = NULL;
331162306a36Sopenharmony_ci		info->tbufs = NULL;
331262306a36Sopenharmony_ci	}
331362306a36Sopenharmony_ci}
331462306a36Sopenharmony_ci
331562306a36Sopenharmony_cistatic int alloc_bufs(struct slgt_info *info, struct slgt_desc *bufs, int count)
331662306a36Sopenharmony_ci{
331762306a36Sopenharmony_ci	int i;
331862306a36Sopenharmony_ci	for (i=0; i < count; i++) {
331962306a36Sopenharmony_ci		bufs[i].buf = dma_alloc_coherent(&info->pdev->dev, DMABUFSIZE,
332062306a36Sopenharmony_ci						 &bufs[i].buf_dma_addr, GFP_KERNEL);
332162306a36Sopenharmony_ci		if (!bufs[i].buf)
332262306a36Sopenharmony_ci			return -ENOMEM;
332362306a36Sopenharmony_ci		bufs[i].pbuf  = cpu_to_le32((unsigned int)bufs[i].buf_dma_addr);
332462306a36Sopenharmony_ci	}
332562306a36Sopenharmony_ci	return 0;
332662306a36Sopenharmony_ci}
332762306a36Sopenharmony_ci
332862306a36Sopenharmony_cistatic void free_bufs(struct slgt_info *info, struct slgt_desc *bufs, int count)
332962306a36Sopenharmony_ci{
333062306a36Sopenharmony_ci	int i;
333162306a36Sopenharmony_ci	for (i=0; i < count; i++) {
333262306a36Sopenharmony_ci		if (bufs[i].buf == NULL)
333362306a36Sopenharmony_ci			continue;
333462306a36Sopenharmony_ci		dma_free_coherent(&info->pdev->dev, DMABUFSIZE, bufs[i].buf,
333562306a36Sopenharmony_ci				  bufs[i].buf_dma_addr);
333662306a36Sopenharmony_ci		bufs[i].buf = NULL;
333762306a36Sopenharmony_ci	}
333862306a36Sopenharmony_ci}
333962306a36Sopenharmony_ci
334062306a36Sopenharmony_cistatic int alloc_dma_bufs(struct slgt_info *info)
334162306a36Sopenharmony_ci{
334262306a36Sopenharmony_ci	info->rbuf_count = 32;
334362306a36Sopenharmony_ci	info->tbuf_count = 32;
334462306a36Sopenharmony_ci
334562306a36Sopenharmony_ci	if (alloc_desc(info) < 0 ||
334662306a36Sopenharmony_ci	    alloc_bufs(info, info->rbufs, info->rbuf_count) < 0 ||
334762306a36Sopenharmony_ci	    alloc_bufs(info, info->tbufs, info->tbuf_count) < 0 ||
334862306a36Sopenharmony_ci	    alloc_tmp_rbuf(info) < 0) {
334962306a36Sopenharmony_ci		DBGERR(("%s DMA buffer alloc fail\n", info->device_name));
335062306a36Sopenharmony_ci		return -ENOMEM;
335162306a36Sopenharmony_ci	}
335262306a36Sopenharmony_ci	reset_rbufs(info);
335362306a36Sopenharmony_ci	return 0;
335462306a36Sopenharmony_ci}
335562306a36Sopenharmony_ci
335662306a36Sopenharmony_cistatic void free_dma_bufs(struct slgt_info *info)
335762306a36Sopenharmony_ci{
335862306a36Sopenharmony_ci	if (info->bufs) {
335962306a36Sopenharmony_ci		free_bufs(info, info->rbufs, info->rbuf_count);
336062306a36Sopenharmony_ci		free_bufs(info, info->tbufs, info->tbuf_count);
336162306a36Sopenharmony_ci		free_desc(info);
336262306a36Sopenharmony_ci	}
336362306a36Sopenharmony_ci	free_tmp_rbuf(info);
336462306a36Sopenharmony_ci}
336562306a36Sopenharmony_ci
336662306a36Sopenharmony_cistatic int claim_resources(struct slgt_info *info)
336762306a36Sopenharmony_ci{
336862306a36Sopenharmony_ci	if (request_mem_region(info->phys_reg_addr, SLGT_REG_SIZE, "synclink_gt") == NULL) {
336962306a36Sopenharmony_ci		DBGERR(("%s reg addr conflict, addr=%08X\n",
337062306a36Sopenharmony_ci			info->device_name, info->phys_reg_addr));
337162306a36Sopenharmony_ci		info->init_error = DiagStatus_AddressConflict;
337262306a36Sopenharmony_ci		goto errout;
337362306a36Sopenharmony_ci	}
337462306a36Sopenharmony_ci	else
337562306a36Sopenharmony_ci		info->reg_addr_requested = true;
337662306a36Sopenharmony_ci
337762306a36Sopenharmony_ci	info->reg_addr = ioremap(info->phys_reg_addr, SLGT_REG_SIZE);
337862306a36Sopenharmony_ci	if (!info->reg_addr) {
337962306a36Sopenharmony_ci		DBGERR(("%s can't map device registers, addr=%08X\n",
338062306a36Sopenharmony_ci			info->device_name, info->phys_reg_addr));
338162306a36Sopenharmony_ci		info->init_error = DiagStatus_CantAssignPciResources;
338262306a36Sopenharmony_ci		goto errout;
338362306a36Sopenharmony_ci	}
338462306a36Sopenharmony_ci	return 0;
338562306a36Sopenharmony_ci
338662306a36Sopenharmony_cierrout:
338762306a36Sopenharmony_ci	release_resources(info);
338862306a36Sopenharmony_ci	return -ENODEV;
338962306a36Sopenharmony_ci}
339062306a36Sopenharmony_ci
339162306a36Sopenharmony_cistatic void release_resources(struct slgt_info *info)
339262306a36Sopenharmony_ci{
339362306a36Sopenharmony_ci	if (info->irq_requested) {
339462306a36Sopenharmony_ci		free_irq(info->irq_level, info);
339562306a36Sopenharmony_ci		info->irq_requested = false;
339662306a36Sopenharmony_ci	}
339762306a36Sopenharmony_ci
339862306a36Sopenharmony_ci	if (info->reg_addr_requested) {
339962306a36Sopenharmony_ci		release_mem_region(info->phys_reg_addr, SLGT_REG_SIZE);
340062306a36Sopenharmony_ci		info->reg_addr_requested = false;
340162306a36Sopenharmony_ci	}
340262306a36Sopenharmony_ci
340362306a36Sopenharmony_ci	if (info->reg_addr) {
340462306a36Sopenharmony_ci		iounmap(info->reg_addr);
340562306a36Sopenharmony_ci		info->reg_addr = NULL;
340662306a36Sopenharmony_ci	}
340762306a36Sopenharmony_ci}
340862306a36Sopenharmony_ci
340962306a36Sopenharmony_ci/* Add the specified device instance data structure to the
341062306a36Sopenharmony_ci * global linked list of devices and increment the device count.
341162306a36Sopenharmony_ci */
341262306a36Sopenharmony_cistatic void add_device(struct slgt_info *info)
341362306a36Sopenharmony_ci{
341462306a36Sopenharmony_ci	char *devstr;
341562306a36Sopenharmony_ci
341662306a36Sopenharmony_ci	info->next_device = NULL;
341762306a36Sopenharmony_ci	info->line = slgt_device_count;
341862306a36Sopenharmony_ci	sprintf(info->device_name, "%s%d", tty_dev_prefix, info->line);
341962306a36Sopenharmony_ci
342062306a36Sopenharmony_ci	if (info->line < MAX_DEVICES) {
342162306a36Sopenharmony_ci		if (maxframe[info->line])
342262306a36Sopenharmony_ci			info->max_frame_size = maxframe[info->line];
342362306a36Sopenharmony_ci	}
342462306a36Sopenharmony_ci
342562306a36Sopenharmony_ci	slgt_device_count++;
342662306a36Sopenharmony_ci
342762306a36Sopenharmony_ci	if (!slgt_device_list)
342862306a36Sopenharmony_ci		slgt_device_list = info;
342962306a36Sopenharmony_ci	else {
343062306a36Sopenharmony_ci		struct slgt_info *current_dev = slgt_device_list;
343162306a36Sopenharmony_ci		while(current_dev->next_device)
343262306a36Sopenharmony_ci			current_dev = current_dev->next_device;
343362306a36Sopenharmony_ci		current_dev->next_device = info;
343462306a36Sopenharmony_ci	}
343562306a36Sopenharmony_ci
343662306a36Sopenharmony_ci	if (info->max_frame_size < 4096)
343762306a36Sopenharmony_ci		info->max_frame_size = 4096;
343862306a36Sopenharmony_ci	else if (info->max_frame_size > 65535)
343962306a36Sopenharmony_ci		info->max_frame_size = 65535;
344062306a36Sopenharmony_ci
344162306a36Sopenharmony_ci	switch(info->pdev->device) {
344262306a36Sopenharmony_ci	case SYNCLINK_GT_DEVICE_ID:
344362306a36Sopenharmony_ci		devstr = "GT";
344462306a36Sopenharmony_ci		break;
344562306a36Sopenharmony_ci	case SYNCLINK_GT2_DEVICE_ID:
344662306a36Sopenharmony_ci		devstr = "GT2";
344762306a36Sopenharmony_ci		break;
344862306a36Sopenharmony_ci	case SYNCLINK_GT4_DEVICE_ID:
344962306a36Sopenharmony_ci		devstr = "GT4";
345062306a36Sopenharmony_ci		break;
345162306a36Sopenharmony_ci	case SYNCLINK_AC_DEVICE_ID:
345262306a36Sopenharmony_ci		devstr = "AC";
345362306a36Sopenharmony_ci		info->params.mode = MGSL_MODE_ASYNC;
345462306a36Sopenharmony_ci		break;
345562306a36Sopenharmony_ci	default:
345662306a36Sopenharmony_ci		devstr = "(unknown model)";
345762306a36Sopenharmony_ci	}
345862306a36Sopenharmony_ci	printk("SyncLink %s %s IO=%08x IRQ=%d MaxFrameSize=%u\n",
345962306a36Sopenharmony_ci		devstr, info->device_name, info->phys_reg_addr,
346062306a36Sopenharmony_ci		info->irq_level, info->max_frame_size);
346162306a36Sopenharmony_ci
346262306a36Sopenharmony_ci#if SYNCLINK_GENERIC_HDLC
346362306a36Sopenharmony_ci	hdlcdev_init(info);
346462306a36Sopenharmony_ci#endif
346562306a36Sopenharmony_ci}
346662306a36Sopenharmony_ci
346762306a36Sopenharmony_cistatic const struct tty_port_operations slgt_port_ops = {
346862306a36Sopenharmony_ci	.carrier_raised = carrier_raised,
346962306a36Sopenharmony_ci	.dtr_rts = dtr_rts,
347062306a36Sopenharmony_ci};
347162306a36Sopenharmony_ci
347262306a36Sopenharmony_ci/*
347362306a36Sopenharmony_ci *  allocate device instance structure, return NULL on failure
347462306a36Sopenharmony_ci */
347562306a36Sopenharmony_cistatic struct slgt_info *alloc_dev(int adapter_num, int port_num, struct pci_dev *pdev)
347662306a36Sopenharmony_ci{
347762306a36Sopenharmony_ci	struct slgt_info *info;
347862306a36Sopenharmony_ci
347962306a36Sopenharmony_ci	info = kzalloc(sizeof(struct slgt_info), GFP_KERNEL);
348062306a36Sopenharmony_ci
348162306a36Sopenharmony_ci	if (!info) {
348262306a36Sopenharmony_ci		DBGERR(("%s device alloc failed adapter=%d port=%d\n",
348362306a36Sopenharmony_ci			driver_name, adapter_num, port_num));
348462306a36Sopenharmony_ci	} else {
348562306a36Sopenharmony_ci		tty_port_init(&info->port);
348662306a36Sopenharmony_ci		info->port.ops = &slgt_port_ops;
348762306a36Sopenharmony_ci		INIT_WORK(&info->task, bh_handler);
348862306a36Sopenharmony_ci		info->max_frame_size = 4096;
348962306a36Sopenharmony_ci		info->base_clock = 14745600;
349062306a36Sopenharmony_ci		info->rbuf_fill_level = DMABUFSIZE;
349162306a36Sopenharmony_ci		init_waitqueue_head(&info->status_event_wait_q);
349262306a36Sopenharmony_ci		init_waitqueue_head(&info->event_wait_q);
349362306a36Sopenharmony_ci		spin_lock_init(&info->netlock);
349462306a36Sopenharmony_ci		memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS));
349562306a36Sopenharmony_ci		info->idle_mode = HDLC_TXIDLE_FLAGS;
349662306a36Sopenharmony_ci		info->adapter_num = adapter_num;
349762306a36Sopenharmony_ci		info->port_num = port_num;
349862306a36Sopenharmony_ci
349962306a36Sopenharmony_ci		timer_setup(&info->tx_timer, tx_timeout, 0);
350062306a36Sopenharmony_ci		timer_setup(&info->rx_timer, rx_timeout, 0);
350162306a36Sopenharmony_ci
350262306a36Sopenharmony_ci		/* Copy configuration info to device instance data */
350362306a36Sopenharmony_ci		info->pdev = pdev;
350462306a36Sopenharmony_ci		info->irq_level = pdev->irq;
350562306a36Sopenharmony_ci		info->phys_reg_addr = pci_resource_start(pdev,0);
350662306a36Sopenharmony_ci
350762306a36Sopenharmony_ci		info->bus_type = MGSL_BUS_TYPE_PCI;
350862306a36Sopenharmony_ci		info->irq_flags = IRQF_SHARED;
350962306a36Sopenharmony_ci
351062306a36Sopenharmony_ci		info->init_error = -1; /* assume error, set to 0 on successful init */
351162306a36Sopenharmony_ci	}
351262306a36Sopenharmony_ci
351362306a36Sopenharmony_ci	return info;
351462306a36Sopenharmony_ci}
351562306a36Sopenharmony_ci
351662306a36Sopenharmony_cistatic void device_init(int adapter_num, struct pci_dev *pdev)
351762306a36Sopenharmony_ci{
351862306a36Sopenharmony_ci	struct slgt_info *port_array[SLGT_MAX_PORTS];
351962306a36Sopenharmony_ci	int i;
352062306a36Sopenharmony_ci	int port_count = 1;
352162306a36Sopenharmony_ci
352262306a36Sopenharmony_ci	if (pdev->device == SYNCLINK_GT2_DEVICE_ID)
352362306a36Sopenharmony_ci		port_count = 2;
352462306a36Sopenharmony_ci	else if (pdev->device == SYNCLINK_GT4_DEVICE_ID)
352562306a36Sopenharmony_ci		port_count = 4;
352662306a36Sopenharmony_ci
352762306a36Sopenharmony_ci	/* allocate device instances for all ports */
352862306a36Sopenharmony_ci	for (i=0; i < port_count; ++i) {
352962306a36Sopenharmony_ci		port_array[i] = alloc_dev(adapter_num, i, pdev);
353062306a36Sopenharmony_ci		if (port_array[i] == NULL) {
353162306a36Sopenharmony_ci			for (--i; i >= 0; --i) {
353262306a36Sopenharmony_ci				tty_port_destroy(&port_array[i]->port);
353362306a36Sopenharmony_ci				kfree(port_array[i]);
353462306a36Sopenharmony_ci			}
353562306a36Sopenharmony_ci			return;
353662306a36Sopenharmony_ci		}
353762306a36Sopenharmony_ci	}
353862306a36Sopenharmony_ci
353962306a36Sopenharmony_ci	/* give copy of port_array to all ports and add to device list  */
354062306a36Sopenharmony_ci	for (i=0; i < port_count; ++i) {
354162306a36Sopenharmony_ci		memcpy(port_array[i]->port_array, port_array, sizeof(port_array));
354262306a36Sopenharmony_ci		add_device(port_array[i]);
354362306a36Sopenharmony_ci		port_array[i]->port_count = port_count;
354462306a36Sopenharmony_ci		spin_lock_init(&port_array[i]->lock);
354562306a36Sopenharmony_ci	}
354662306a36Sopenharmony_ci
354762306a36Sopenharmony_ci	/* Allocate and claim adapter resources */
354862306a36Sopenharmony_ci	if (!claim_resources(port_array[0])) {
354962306a36Sopenharmony_ci
355062306a36Sopenharmony_ci		alloc_dma_bufs(port_array[0]);
355162306a36Sopenharmony_ci
355262306a36Sopenharmony_ci		/* copy resource information from first port to others */
355362306a36Sopenharmony_ci		for (i = 1; i < port_count; ++i) {
355462306a36Sopenharmony_ci			port_array[i]->irq_level = port_array[0]->irq_level;
355562306a36Sopenharmony_ci			port_array[i]->reg_addr  = port_array[0]->reg_addr;
355662306a36Sopenharmony_ci			alloc_dma_bufs(port_array[i]);
355762306a36Sopenharmony_ci		}
355862306a36Sopenharmony_ci
355962306a36Sopenharmony_ci		if (request_irq(port_array[0]->irq_level,
356062306a36Sopenharmony_ci					slgt_interrupt,
356162306a36Sopenharmony_ci					port_array[0]->irq_flags,
356262306a36Sopenharmony_ci					port_array[0]->device_name,
356362306a36Sopenharmony_ci					port_array[0]) < 0) {
356462306a36Sopenharmony_ci			DBGERR(("%s request_irq failed IRQ=%d\n",
356562306a36Sopenharmony_ci				port_array[0]->device_name,
356662306a36Sopenharmony_ci				port_array[0]->irq_level));
356762306a36Sopenharmony_ci		} else {
356862306a36Sopenharmony_ci			port_array[0]->irq_requested = true;
356962306a36Sopenharmony_ci			adapter_test(port_array[0]);
357062306a36Sopenharmony_ci			for (i=1 ; i < port_count ; i++) {
357162306a36Sopenharmony_ci				port_array[i]->init_error = port_array[0]->init_error;
357262306a36Sopenharmony_ci				port_array[i]->gpio_present = port_array[0]->gpio_present;
357362306a36Sopenharmony_ci			}
357462306a36Sopenharmony_ci		}
357562306a36Sopenharmony_ci	}
357662306a36Sopenharmony_ci
357762306a36Sopenharmony_ci	for (i = 0; i < port_count; ++i) {
357862306a36Sopenharmony_ci		struct slgt_info *info = port_array[i];
357962306a36Sopenharmony_ci		tty_port_register_device(&info->port, serial_driver, info->line,
358062306a36Sopenharmony_ci				&info->pdev->dev);
358162306a36Sopenharmony_ci	}
358262306a36Sopenharmony_ci}
358362306a36Sopenharmony_ci
358462306a36Sopenharmony_cistatic int init_one(struct pci_dev *dev,
358562306a36Sopenharmony_ci			      const struct pci_device_id *ent)
358662306a36Sopenharmony_ci{
358762306a36Sopenharmony_ci	if (pci_enable_device(dev)) {
358862306a36Sopenharmony_ci		printk("error enabling pci device %p\n", dev);
358962306a36Sopenharmony_ci		return -EIO;
359062306a36Sopenharmony_ci	}
359162306a36Sopenharmony_ci	pci_set_master(dev);
359262306a36Sopenharmony_ci	device_init(slgt_device_count, dev);
359362306a36Sopenharmony_ci	return 0;
359462306a36Sopenharmony_ci}
359562306a36Sopenharmony_ci
359662306a36Sopenharmony_cistatic void remove_one(struct pci_dev *dev)
359762306a36Sopenharmony_ci{
359862306a36Sopenharmony_ci}
359962306a36Sopenharmony_ci
360062306a36Sopenharmony_cistatic const struct tty_operations ops = {
360162306a36Sopenharmony_ci	.open = open,
360262306a36Sopenharmony_ci	.close = close,
360362306a36Sopenharmony_ci	.write = write,
360462306a36Sopenharmony_ci	.put_char = put_char,
360562306a36Sopenharmony_ci	.flush_chars = flush_chars,
360662306a36Sopenharmony_ci	.write_room = write_room,
360762306a36Sopenharmony_ci	.chars_in_buffer = chars_in_buffer,
360862306a36Sopenharmony_ci	.flush_buffer = flush_buffer,
360962306a36Sopenharmony_ci	.ioctl = ioctl,
361062306a36Sopenharmony_ci	.compat_ioctl = slgt_compat_ioctl,
361162306a36Sopenharmony_ci	.throttle = throttle,
361262306a36Sopenharmony_ci	.unthrottle = unthrottle,
361362306a36Sopenharmony_ci	.send_xchar = send_xchar,
361462306a36Sopenharmony_ci	.break_ctl = set_break,
361562306a36Sopenharmony_ci	.wait_until_sent = wait_until_sent,
361662306a36Sopenharmony_ci	.set_termios = set_termios,
361762306a36Sopenharmony_ci	.stop = tx_hold,
361862306a36Sopenharmony_ci	.start = tx_release,
361962306a36Sopenharmony_ci	.hangup = hangup,
362062306a36Sopenharmony_ci	.tiocmget = tiocmget,
362162306a36Sopenharmony_ci	.tiocmset = tiocmset,
362262306a36Sopenharmony_ci	.get_icount = get_icount,
362362306a36Sopenharmony_ci	.proc_show = synclink_gt_proc_show,
362462306a36Sopenharmony_ci};
362562306a36Sopenharmony_ci
362662306a36Sopenharmony_cistatic void slgt_cleanup(void)
362762306a36Sopenharmony_ci{
362862306a36Sopenharmony_ci	struct slgt_info *info;
362962306a36Sopenharmony_ci	struct slgt_info *tmp;
363062306a36Sopenharmony_ci
363162306a36Sopenharmony_ci	if (serial_driver) {
363262306a36Sopenharmony_ci		for (info=slgt_device_list ; info != NULL ; info=info->next_device)
363362306a36Sopenharmony_ci			tty_unregister_device(serial_driver, info->line);
363462306a36Sopenharmony_ci		tty_unregister_driver(serial_driver);
363562306a36Sopenharmony_ci		tty_driver_kref_put(serial_driver);
363662306a36Sopenharmony_ci	}
363762306a36Sopenharmony_ci
363862306a36Sopenharmony_ci	/* reset devices */
363962306a36Sopenharmony_ci	info = slgt_device_list;
364062306a36Sopenharmony_ci	while(info) {
364162306a36Sopenharmony_ci		reset_port(info);
364262306a36Sopenharmony_ci		info = info->next_device;
364362306a36Sopenharmony_ci	}
364462306a36Sopenharmony_ci
364562306a36Sopenharmony_ci	/* release devices */
364662306a36Sopenharmony_ci	info = slgt_device_list;
364762306a36Sopenharmony_ci	while(info) {
364862306a36Sopenharmony_ci#if SYNCLINK_GENERIC_HDLC
364962306a36Sopenharmony_ci		hdlcdev_exit(info);
365062306a36Sopenharmony_ci#endif
365162306a36Sopenharmony_ci		free_dma_bufs(info);
365262306a36Sopenharmony_ci		free_tmp_rbuf(info);
365362306a36Sopenharmony_ci		if (info->port_num == 0)
365462306a36Sopenharmony_ci			release_resources(info);
365562306a36Sopenharmony_ci		tmp = info;
365662306a36Sopenharmony_ci		info = info->next_device;
365762306a36Sopenharmony_ci		tty_port_destroy(&tmp->port);
365862306a36Sopenharmony_ci		kfree(tmp);
365962306a36Sopenharmony_ci	}
366062306a36Sopenharmony_ci
366162306a36Sopenharmony_ci	if (pci_registered)
366262306a36Sopenharmony_ci		pci_unregister_driver(&pci_driver);
366362306a36Sopenharmony_ci}
366462306a36Sopenharmony_ci
366562306a36Sopenharmony_ci/*
366662306a36Sopenharmony_ci *  Driver initialization entry point.
366762306a36Sopenharmony_ci */
366862306a36Sopenharmony_cistatic int __init slgt_init(void)
366962306a36Sopenharmony_ci{
367062306a36Sopenharmony_ci	int rc;
367162306a36Sopenharmony_ci
367262306a36Sopenharmony_ci	serial_driver = tty_alloc_driver(MAX_DEVICES, TTY_DRIVER_REAL_RAW |
367362306a36Sopenharmony_ci			TTY_DRIVER_DYNAMIC_DEV);
367462306a36Sopenharmony_ci	if (IS_ERR(serial_driver)) {
367562306a36Sopenharmony_ci		printk("%s can't allocate tty driver\n", driver_name);
367662306a36Sopenharmony_ci		return PTR_ERR(serial_driver);
367762306a36Sopenharmony_ci	}
367862306a36Sopenharmony_ci
367962306a36Sopenharmony_ci	/* Initialize the tty_driver structure */
368062306a36Sopenharmony_ci
368162306a36Sopenharmony_ci	serial_driver->driver_name = "synclink_gt";
368262306a36Sopenharmony_ci	serial_driver->name = tty_dev_prefix;
368362306a36Sopenharmony_ci	serial_driver->major = ttymajor;
368462306a36Sopenharmony_ci	serial_driver->minor_start = 64;
368562306a36Sopenharmony_ci	serial_driver->type = TTY_DRIVER_TYPE_SERIAL;
368662306a36Sopenharmony_ci	serial_driver->subtype = SERIAL_TYPE_NORMAL;
368762306a36Sopenharmony_ci	serial_driver->init_termios = tty_std_termios;
368862306a36Sopenharmony_ci	serial_driver->init_termios.c_cflag =
368962306a36Sopenharmony_ci		B9600 | CS8 | CREAD | HUPCL | CLOCAL;
369062306a36Sopenharmony_ci	serial_driver->init_termios.c_ispeed = 9600;
369162306a36Sopenharmony_ci	serial_driver->init_termios.c_ospeed = 9600;
369262306a36Sopenharmony_ci	tty_set_operations(serial_driver, &ops);
369362306a36Sopenharmony_ci	if ((rc = tty_register_driver(serial_driver)) < 0) {
369462306a36Sopenharmony_ci		DBGERR(("%s can't register serial driver\n", driver_name));
369562306a36Sopenharmony_ci		tty_driver_kref_put(serial_driver);
369662306a36Sopenharmony_ci		serial_driver = NULL;
369762306a36Sopenharmony_ci		goto error;
369862306a36Sopenharmony_ci	}
369962306a36Sopenharmony_ci
370062306a36Sopenharmony_ci	slgt_device_count = 0;
370162306a36Sopenharmony_ci	if ((rc = pci_register_driver(&pci_driver)) < 0) {
370262306a36Sopenharmony_ci		printk("%s pci_register_driver error=%d\n", driver_name, rc);
370362306a36Sopenharmony_ci		goto error;
370462306a36Sopenharmony_ci	}
370562306a36Sopenharmony_ci	pci_registered = true;
370662306a36Sopenharmony_ci
370762306a36Sopenharmony_ci	return 0;
370862306a36Sopenharmony_ci
370962306a36Sopenharmony_cierror:
371062306a36Sopenharmony_ci	slgt_cleanup();
371162306a36Sopenharmony_ci	return rc;
371262306a36Sopenharmony_ci}
371362306a36Sopenharmony_ci
371462306a36Sopenharmony_cistatic void __exit slgt_exit(void)
371562306a36Sopenharmony_ci{
371662306a36Sopenharmony_ci	slgt_cleanup();
371762306a36Sopenharmony_ci}
371862306a36Sopenharmony_ci
371962306a36Sopenharmony_cimodule_init(slgt_init);
372062306a36Sopenharmony_cimodule_exit(slgt_exit);
372162306a36Sopenharmony_ci
372262306a36Sopenharmony_ci/*
372362306a36Sopenharmony_ci * register access routines
372462306a36Sopenharmony_ci */
372562306a36Sopenharmony_ci
372662306a36Sopenharmony_cistatic inline void __iomem *calc_regaddr(struct slgt_info *info,
372762306a36Sopenharmony_ci					 unsigned int addr)
372862306a36Sopenharmony_ci{
372962306a36Sopenharmony_ci	void __iomem *reg_addr = info->reg_addr + addr;
373062306a36Sopenharmony_ci
373162306a36Sopenharmony_ci	if (addr >= 0x80)
373262306a36Sopenharmony_ci		reg_addr += info->port_num * 32;
373362306a36Sopenharmony_ci	else if (addr >= 0x40)
373462306a36Sopenharmony_ci		reg_addr += info->port_num * 16;
373562306a36Sopenharmony_ci
373662306a36Sopenharmony_ci	return reg_addr;
373762306a36Sopenharmony_ci}
373862306a36Sopenharmony_ci
373962306a36Sopenharmony_cistatic __u8 rd_reg8(struct slgt_info *info, unsigned int addr)
374062306a36Sopenharmony_ci{
374162306a36Sopenharmony_ci	return readb(calc_regaddr(info, addr));
374262306a36Sopenharmony_ci}
374362306a36Sopenharmony_ci
374462306a36Sopenharmony_cistatic void wr_reg8(struct slgt_info *info, unsigned int addr, __u8 value)
374562306a36Sopenharmony_ci{
374662306a36Sopenharmony_ci	writeb(value, calc_regaddr(info, addr));
374762306a36Sopenharmony_ci}
374862306a36Sopenharmony_ci
374962306a36Sopenharmony_cistatic __u16 rd_reg16(struct slgt_info *info, unsigned int addr)
375062306a36Sopenharmony_ci{
375162306a36Sopenharmony_ci	return readw(calc_regaddr(info, addr));
375262306a36Sopenharmony_ci}
375362306a36Sopenharmony_ci
375462306a36Sopenharmony_cistatic void wr_reg16(struct slgt_info *info, unsigned int addr, __u16 value)
375562306a36Sopenharmony_ci{
375662306a36Sopenharmony_ci	writew(value, calc_regaddr(info, addr));
375762306a36Sopenharmony_ci}
375862306a36Sopenharmony_ci
375962306a36Sopenharmony_cistatic __u32 rd_reg32(struct slgt_info *info, unsigned int addr)
376062306a36Sopenharmony_ci{
376162306a36Sopenharmony_ci	return readl(calc_regaddr(info, addr));
376262306a36Sopenharmony_ci}
376362306a36Sopenharmony_ci
376462306a36Sopenharmony_cistatic void wr_reg32(struct slgt_info *info, unsigned int addr, __u32 value)
376562306a36Sopenharmony_ci{
376662306a36Sopenharmony_ci	writel(value, calc_regaddr(info, addr));
376762306a36Sopenharmony_ci}
376862306a36Sopenharmony_ci
376962306a36Sopenharmony_cistatic void rdma_reset(struct slgt_info *info)
377062306a36Sopenharmony_ci{
377162306a36Sopenharmony_ci	unsigned int i;
377262306a36Sopenharmony_ci
377362306a36Sopenharmony_ci	/* set reset bit */
377462306a36Sopenharmony_ci	wr_reg32(info, RDCSR, BIT1);
377562306a36Sopenharmony_ci
377662306a36Sopenharmony_ci	/* wait for enable bit cleared */
377762306a36Sopenharmony_ci	for(i=0 ; i < 1000 ; i++)
377862306a36Sopenharmony_ci		if (!(rd_reg32(info, RDCSR) & BIT0))
377962306a36Sopenharmony_ci			break;
378062306a36Sopenharmony_ci}
378162306a36Sopenharmony_ci
378262306a36Sopenharmony_cistatic void tdma_reset(struct slgt_info *info)
378362306a36Sopenharmony_ci{
378462306a36Sopenharmony_ci	unsigned int i;
378562306a36Sopenharmony_ci
378662306a36Sopenharmony_ci	/* set reset bit */
378762306a36Sopenharmony_ci	wr_reg32(info, TDCSR, BIT1);
378862306a36Sopenharmony_ci
378962306a36Sopenharmony_ci	/* wait for enable bit cleared */
379062306a36Sopenharmony_ci	for(i=0 ; i < 1000 ; i++)
379162306a36Sopenharmony_ci		if (!(rd_reg32(info, TDCSR) & BIT0))
379262306a36Sopenharmony_ci			break;
379362306a36Sopenharmony_ci}
379462306a36Sopenharmony_ci
379562306a36Sopenharmony_ci/*
379662306a36Sopenharmony_ci * enable internal loopback
379762306a36Sopenharmony_ci * TxCLK and RxCLK are generated from BRG
379862306a36Sopenharmony_ci * and TxD is looped back to RxD internally.
379962306a36Sopenharmony_ci */
380062306a36Sopenharmony_cistatic void enable_loopback(struct slgt_info *info)
380162306a36Sopenharmony_ci{
380262306a36Sopenharmony_ci	/* SCR (serial control) BIT2=loopback enable */
380362306a36Sopenharmony_ci	wr_reg16(info, SCR, (unsigned short)(rd_reg16(info, SCR) | BIT2));
380462306a36Sopenharmony_ci
380562306a36Sopenharmony_ci	if (info->params.mode != MGSL_MODE_ASYNC) {
380662306a36Sopenharmony_ci		/* CCR (clock control)
380762306a36Sopenharmony_ci		 * 07..05  tx clock source (010 = BRG)
380862306a36Sopenharmony_ci		 * 04..02  rx clock source (010 = BRG)
380962306a36Sopenharmony_ci		 * 01      auxclk enable   (0 = disable)
381062306a36Sopenharmony_ci		 * 00      BRG enable      (1 = enable)
381162306a36Sopenharmony_ci		 *
381262306a36Sopenharmony_ci		 * 0100 1001
381362306a36Sopenharmony_ci		 */
381462306a36Sopenharmony_ci		wr_reg8(info, CCR, 0x49);
381562306a36Sopenharmony_ci
381662306a36Sopenharmony_ci		/* set speed if available, otherwise use default */
381762306a36Sopenharmony_ci		if (info->params.clock_speed)
381862306a36Sopenharmony_ci			set_rate(info, info->params.clock_speed);
381962306a36Sopenharmony_ci		else
382062306a36Sopenharmony_ci			set_rate(info, 3686400);
382162306a36Sopenharmony_ci	}
382262306a36Sopenharmony_ci}
382362306a36Sopenharmony_ci
382462306a36Sopenharmony_ci/*
382562306a36Sopenharmony_ci *  set baud rate generator to specified rate
382662306a36Sopenharmony_ci */
382762306a36Sopenharmony_cistatic void set_rate(struct slgt_info *info, u32 rate)
382862306a36Sopenharmony_ci{
382962306a36Sopenharmony_ci	unsigned int div;
383062306a36Sopenharmony_ci	unsigned int osc = info->base_clock;
383162306a36Sopenharmony_ci
383262306a36Sopenharmony_ci	/* div = osc/rate - 1
383362306a36Sopenharmony_ci	 *
383462306a36Sopenharmony_ci	 * Round div up if osc/rate is not integer to
383562306a36Sopenharmony_ci	 * force to next slowest rate.
383662306a36Sopenharmony_ci	 */
383762306a36Sopenharmony_ci
383862306a36Sopenharmony_ci	if (rate) {
383962306a36Sopenharmony_ci		div = osc/rate;
384062306a36Sopenharmony_ci		if (!(osc % rate) && div)
384162306a36Sopenharmony_ci			div--;
384262306a36Sopenharmony_ci		wr_reg16(info, BDR, (unsigned short)div);
384362306a36Sopenharmony_ci	}
384462306a36Sopenharmony_ci}
384562306a36Sopenharmony_ci
384662306a36Sopenharmony_cistatic void rx_stop(struct slgt_info *info)
384762306a36Sopenharmony_ci{
384862306a36Sopenharmony_ci	unsigned short val;
384962306a36Sopenharmony_ci
385062306a36Sopenharmony_ci	/* disable and reset receiver */
385162306a36Sopenharmony_ci	val = rd_reg16(info, RCR) & ~BIT1;          /* clear enable bit */
385262306a36Sopenharmony_ci	wr_reg16(info, RCR, (unsigned short)(val | BIT2)); /* set reset bit */
385362306a36Sopenharmony_ci	wr_reg16(info, RCR, val);                  /* clear reset bit */
385462306a36Sopenharmony_ci
385562306a36Sopenharmony_ci	slgt_irq_off(info, IRQ_RXOVER + IRQ_RXDATA + IRQ_RXIDLE);
385662306a36Sopenharmony_ci
385762306a36Sopenharmony_ci	/* clear pending rx interrupts */
385862306a36Sopenharmony_ci	wr_reg16(info, SSR, IRQ_RXIDLE + IRQ_RXOVER);
385962306a36Sopenharmony_ci
386062306a36Sopenharmony_ci	rdma_reset(info);
386162306a36Sopenharmony_ci
386262306a36Sopenharmony_ci	info->rx_enabled = false;
386362306a36Sopenharmony_ci	info->rx_restart = false;
386462306a36Sopenharmony_ci}
386562306a36Sopenharmony_ci
386662306a36Sopenharmony_cistatic void rx_start(struct slgt_info *info)
386762306a36Sopenharmony_ci{
386862306a36Sopenharmony_ci	unsigned short val;
386962306a36Sopenharmony_ci
387062306a36Sopenharmony_ci	slgt_irq_off(info, IRQ_RXOVER + IRQ_RXDATA);
387162306a36Sopenharmony_ci
387262306a36Sopenharmony_ci	/* clear pending rx overrun IRQ */
387362306a36Sopenharmony_ci	wr_reg16(info, SSR, IRQ_RXOVER);
387462306a36Sopenharmony_ci
387562306a36Sopenharmony_ci	/* reset and disable receiver */
387662306a36Sopenharmony_ci	val = rd_reg16(info, RCR) & ~BIT1; /* clear enable bit */
387762306a36Sopenharmony_ci	wr_reg16(info, RCR, (unsigned short)(val | BIT2)); /* set reset bit */
387862306a36Sopenharmony_ci	wr_reg16(info, RCR, val);                  /* clear reset bit */
387962306a36Sopenharmony_ci
388062306a36Sopenharmony_ci	rdma_reset(info);
388162306a36Sopenharmony_ci	reset_rbufs(info);
388262306a36Sopenharmony_ci
388362306a36Sopenharmony_ci	if (info->rx_pio) {
388462306a36Sopenharmony_ci		/* rx request when rx FIFO not empty */
388562306a36Sopenharmony_ci		wr_reg16(info, SCR, (unsigned short)(rd_reg16(info, SCR) & ~BIT14));
388662306a36Sopenharmony_ci		slgt_irq_on(info, IRQ_RXDATA);
388762306a36Sopenharmony_ci		if (info->params.mode == MGSL_MODE_ASYNC) {
388862306a36Sopenharmony_ci			/* enable saving of rx status */
388962306a36Sopenharmony_ci			wr_reg32(info, RDCSR, BIT6);
389062306a36Sopenharmony_ci		}
389162306a36Sopenharmony_ci	} else {
389262306a36Sopenharmony_ci		/* rx request when rx FIFO half full */
389362306a36Sopenharmony_ci		wr_reg16(info, SCR, (unsigned short)(rd_reg16(info, SCR) | BIT14));
389462306a36Sopenharmony_ci		/* set 1st descriptor address */
389562306a36Sopenharmony_ci		wr_reg32(info, RDDAR, info->rbufs[0].pdesc);
389662306a36Sopenharmony_ci
389762306a36Sopenharmony_ci		if (info->params.mode != MGSL_MODE_ASYNC) {
389862306a36Sopenharmony_ci			/* enable rx DMA and DMA interrupt */
389962306a36Sopenharmony_ci			wr_reg32(info, RDCSR, (BIT2 + BIT0));
390062306a36Sopenharmony_ci		} else {
390162306a36Sopenharmony_ci			/* enable saving of rx status, rx DMA and DMA interrupt */
390262306a36Sopenharmony_ci			wr_reg32(info, RDCSR, (BIT6 + BIT2 + BIT0));
390362306a36Sopenharmony_ci		}
390462306a36Sopenharmony_ci	}
390562306a36Sopenharmony_ci
390662306a36Sopenharmony_ci	slgt_irq_on(info, IRQ_RXOVER);
390762306a36Sopenharmony_ci
390862306a36Sopenharmony_ci	/* enable receiver */
390962306a36Sopenharmony_ci	wr_reg16(info, RCR, (unsigned short)(rd_reg16(info, RCR) | BIT1));
391062306a36Sopenharmony_ci
391162306a36Sopenharmony_ci	info->rx_restart = false;
391262306a36Sopenharmony_ci	info->rx_enabled = true;
391362306a36Sopenharmony_ci}
391462306a36Sopenharmony_ci
391562306a36Sopenharmony_cistatic void tx_start(struct slgt_info *info)
391662306a36Sopenharmony_ci{
391762306a36Sopenharmony_ci	if (!info->tx_enabled) {
391862306a36Sopenharmony_ci		wr_reg16(info, TCR,
391962306a36Sopenharmony_ci			 (unsigned short)((rd_reg16(info, TCR) | BIT1) & ~BIT2));
392062306a36Sopenharmony_ci		info->tx_enabled = true;
392162306a36Sopenharmony_ci	}
392262306a36Sopenharmony_ci
392362306a36Sopenharmony_ci	if (desc_count(info->tbufs[info->tbuf_start])) {
392462306a36Sopenharmony_ci		info->drop_rts_on_tx_done = false;
392562306a36Sopenharmony_ci
392662306a36Sopenharmony_ci		if (info->params.mode != MGSL_MODE_ASYNC) {
392762306a36Sopenharmony_ci			if (info->params.flags & HDLC_FLAG_AUTO_RTS) {
392862306a36Sopenharmony_ci				get_gtsignals(info);
392962306a36Sopenharmony_ci				if (!(info->signals & SerialSignal_RTS)) {
393062306a36Sopenharmony_ci					info->signals |= SerialSignal_RTS;
393162306a36Sopenharmony_ci					set_gtsignals(info);
393262306a36Sopenharmony_ci					info->drop_rts_on_tx_done = true;
393362306a36Sopenharmony_ci				}
393462306a36Sopenharmony_ci			}
393562306a36Sopenharmony_ci
393662306a36Sopenharmony_ci			slgt_irq_off(info, IRQ_TXDATA);
393762306a36Sopenharmony_ci			slgt_irq_on(info, IRQ_TXUNDER + IRQ_TXIDLE);
393862306a36Sopenharmony_ci			/* clear tx idle and underrun status bits */
393962306a36Sopenharmony_ci			wr_reg16(info, SSR, (unsigned short)(IRQ_TXIDLE + IRQ_TXUNDER));
394062306a36Sopenharmony_ci		} else {
394162306a36Sopenharmony_ci			slgt_irq_off(info, IRQ_TXDATA);
394262306a36Sopenharmony_ci			slgt_irq_on(info, IRQ_TXIDLE);
394362306a36Sopenharmony_ci			/* clear tx idle status bit */
394462306a36Sopenharmony_ci			wr_reg16(info, SSR, IRQ_TXIDLE);
394562306a36Sopenharmony_ci		}
394662306a36Sopenharmony_ci		/* set 1st descriptor address and start DMA */
394762306a36Sopenharmony_ci		wr_reg32(info, TDDAR, info->tbufs[info->tbuf_start].pdesc);
394862306a36Sopenharmony_ci		wr_reg32(info, TDCSR, BIT2 + BIT0);
394962306a36Sopenharmony_ci		info->tx_active = true;
395062306a36Sopenharmony_ci	}
395162306a36Sopenharmony_ci}
395262306a36Sopenharmony_ci
395362306a36Sopenharmony_cistatic void tx_stop(struct slgt_info *info)
395462306a36Sopenharmony_ci{
395562306a36Sopenharmony_ci	unsigned short val;
395662306a36Sopenharmony_ci
395762306a36Sopenharmony_ci	del_timer(&info->tx_timer);
395862306a36Sopenharmony_ci
395962306a36Sopenharmony_ci	tdma_reset(info);
396062306a36Sopenharmony_ci
396162306a36Sopenharmony_ci	/* reset and disable transmitter */
396262306a36Sopenharmony_ci	val = rd_reg16(info, TCR) & ~BIT1;          /* clear enable bit */
396362306a36Sopenharmony_ci	wr_reg16(info, TCR, (unsigned short)(val | BIT2)); /* set reset bit */
396462306a36Sopenharmony_ci
396562306a36Sopenharmony_ci	slgt_irq_off(info, IRQ_TXDATA + IRQ_TXIDLE + IRQ_TXUNDER);
396662306a36Sopenharmony_ci
396762306a36Sopenharmony_ci	/* clear tx idle and underrun status bit */
396862306a36Sopenharmony_ci	wr_reg16(info, SSR, (unsigned short)(IRQ_TXIDLE + IRQ_TXUNDER));
396962306a36Sopenharmony_ci
397062306a36Sopenharmony_ci	reset_tbufs(info);
397162306a36Sopenharmony_ci
397262306a36Sopenharmony_ci	info->tx_enabled = false;
397362306a36Sopenharmony_ci	info->tx_active = false;
397462306a36Sopenharmony_ci}
397562306a36Sopenharmony_ci
397662306a36Sopenharmony_cistatic void reset_port(struct slgt_info *info)
397762306a36Sopenharmony_ci{
397862306a36Sopenharmony_ci	if (!info->reg_addr)
397962306a36Sopenharmony_ci		return;
398062306a36Sopenharmony_ci
398162306a36Sopenharmony_ci	tx_stop(info);
398262306a36Sopenharmony_ci	rx_stop(info);
398362306a36Sopenharmony_ci
398462306a36Sopenharmony_ci	info->signals &= ~(SerialSignal_RTS | SerialSignal_DTR);
398562306a36Sopenharmony_ci	set_gtsignals(info);
398662306a36Sopenharmony_ci
398762306a36Sopenharmony_ci	slgt_irq_off(info, IRQ_ALL | IRQ_MASTER);
398862306a36Sopenharmony_ci}
398962306a36Sopenharmony_ci
399062306a36Sopenharmony_cistatic void reset_adapter(struct slgt_info *info)
399162306a36Sopenharmony_ci{
399262306a36Sopenharmony_ci	int i;
399362306a36Sopenharmony_ci	for (i=0; i < info->port_count; ++i) {
399462306a36Sopenharmony_ci		if (info->port_array[i])
399562306a36Sopenharmony_ci			reset_port(info->port_array[i]);
399662306a36Sopenharmony_ci	}
399762306a36Sopenharmony_ci}
399862306a36Sopenharmony_ci
399962306a36Sopenharmony_cistatic void async_mode(struct slgt_info *info)
400062306a36Sopenharmony_ci{
400162306a36Sopenharmony_ci  	unsigned short val;
400262306a36Sopenharmony_ci
400362306a36Sopenharmony_ci	slgt_irq_off(info, IRQ_ALL | IRQ_MASTER);
400462306a36Sopenharmony_ci	tx_stop(info);
400562306a36Sopenharmony_ci	rx_stop(info);
400662306a36Sopenharmony_ci
400762306a36Sopenharmony_ci	/* TCR (tx control)
400862306a36Sopenharmony_ci	 *
400962306a36Sopenharmony_ci	 * 15..13  mode, 010=async
401062306a36Sopenharmony_ci	 * 12..10  encoding, 000=NRZ
401162306a36Sopenharmony_ci	 * 09      parity enable
401262306a36Sopenharmony_ci	 * 08      1=odd parity, 0=even parity
401362306a36Sopenharmony_ci	 * 07      1=RTS driver control
401462306a36Sopenharmony_ci	 * 06      1=break enable
401562306a36Sopenharmony_ci	 * 05..04  character length
401662306a36Sopenharmony_ci	 *         00=5 bits
401762306a36Sopenharmony_ci	 *         01=6 bits
401862306a36Sopenharmony_ci	 *         10=7 bits
401962306a36Sopenharmony_ci	 *         11=8 bits
402062306a36Sopenharmony_ci	 * 03      0=1 stop bit, 1=2 stop bits
402162306a36Sopenharmony_ci	 * 02      reset
402262306a36Sopenharmony_ci	 * 01      enable
402362306a36Sopenharmony_ci	 * 00      auto-CTS enable
402462306a36Sopenharmony_ci	 */
402562306a36Sopenharmony_ci	val = 0x4000;
402662306a36Sopenharmony_ci
402762306a36Sopenharmony_ci	if (info->if_mode & MGSL_INTERFACE_RTS_EN)
402862306a36Sopenharmony_ci		val |= BIT7;
402962306a36Sopenharmony_ci
403062306a36Sopenharmony_ci	if (info->params.parity != ASYNC_PARITY_NONE) {
403162306a36Sopenharmony_ci		val |= BIT9;
403262306a36Sopenharmony_ci		if (info->params.parity == ASYNC_PARITY_ODD)
403362306a36Sopenharmony_ci			val |= BIT8;
403462306a36Sopenharmony_ci	}
403562306a36Sopenharmony_ci
403662306a36Sopenharmony_ci	switch (info->params.data_bits)
403762306a36Sopenharmony_ci	{
403862306a36Sopenharmony_ci	case 6: val |= BIT4; break;
403962306a36Sopenharmony_ci	case 7: val |= BIT5; break;
404062306a36Sopenharmony_ci	case 8: val |= BIT5 + BIT4; break;
404162306a36Sopenharmony_ci	}
404262306a36Sopenharmony_ci
404362306a36Sopenharmony_ci	if (info->params.stop_bits != 1)
404462306a36Sopenharmony_ci		val |= BIT3;
404562306a36Sopenharmony_ci
404662306a36Sopenharmony_ci	if (info->params.flags & HDLC_FLAG_AUTO_CTS)
404762306a36Sopenharmony_ci		val |= BIT0;
404862306a36Sopenharmony_ci
404962306a36Sopenharmony_ci	wr_reg16(info, TCR, val);
405062306a36Sopenharmony_ci
405162306a36Sopenharmony_ci	/* RCR (rx control)
405262306a36Sopenharmony_ci	 *
405362306a36Sopenharmony_ci	 * 15..13  mode, 010=async
405462306a36Sopenharmony_ci	 * 12..10  encoding, 000=NRZ
405562306a36Sopenharmony_ci	 * 09      parity enable
405662306a36Sopenharmony_ci	 * 08      1=odd parity, 0=even parity
405762306a36Sopenharmony_ci	 * 07..06  reserved, must be 0
405862306a36Sopenharmony_ci	 * 05..04  character length
405962306a36Sopenharmony_ci	 *         00=5 bits
406062306a36Sopenharmony_ci	 *         01=6 bits
406162306a36Sopenharmony_ci	 *         10=7 bits
406262306a36Sopenharmony_ci	 *         11=8 bits
406362306a36Sopenharmony_ci	 * 03      reserved, must be zero
406462306a36Sopenharmony_ci	 * 02      reset
406562306a36Sopenharmony_ci	 * 01      enable
406662306a36Sopenharmony_ci	 * 00      auto-DCD enable
406762306a36Sopenharmony_ci	 */
406862306a36Sopenharmony_ci	val = 0x4000;
406962306a36Sopenharmony_ci
407062306a36Sopenharmony_ci	if (info->params.parity != ASYNC_PARITY_NONE) {
407162306a36Sopenharmony_ci		val |= BIT9;
407262306a36Sopenharmony_ci		if (info->params.parity == ASYNC_PARITY_ODD)
407362306a36Sopenharmony_ci			val |= BIT8;
407462306a36Sopenharmony_ci	}
407562306a36Sopenharmony_ci
407662306a36Sopenharmony_ci	switch (info->params.data_bits)
407762306a36Sopenharmony_ci	{
407862306a36Sopenharmony_ci	case 6: val |= BIT4; break;
407962306a36Sopenharmony_ci	case 7: val |= BIT5; break;
408062306a36Sopenharmony_ci	case 8: val |= BIT5 + BIT4; break;
408162306a36Sopenharmony_ci	}
408262306a36Sopenharmony_ci
408362306a36Sopenharmony_ci	if (info->params.flags & HDLC_FLAG_AUTO_DCD)
408462306a36Sopenharmony_ci		val |= BIT0;
408562306a36Sopenharmony_ci
408662306a36Sopenharmony_ci	wr_reg16(info, RCR, val);
408762306a36Sopenharmony_ci
408862306a36Sopenharmony_ci	/* CCR (clock control)
408962306a36Sopenharmony_ci	 *
409062306a36Sopenharmony_ci	 * 07..05  011 = tx clock source is BRG/16
409162306a36Sopenharmony_ci	 * 04..02  010 = rx clock source is BRG
409262306a36Sopenharmony_ci	 * 01      0 = auxclk disabled
409362306a36Sopenharmony_ci	 * 00      1 = BRG enabled
409462306a36Sopenharmony_ci	 *
409562306a36Sopenharmony_ci	 * 0110 1001
409662306a36Sopenharmony_ci	 */
409762306a36Sopenharmony_ci	wr_reg8(info, CCR, 0x69);
409862306a36Sopenharmony_ci
409962306a36Sopenharmony_ci	msc_set_vcr(info);
410062306a36Sopenharmony_ci
410162306a36Sopenharmony_ci	/* SCR (serial control)
410262306a36Sopenharmony_ci	 *
410362306a36Sopenharmony_ci	 * 15  1=tx req on FIFO half empty
410462306a36Sopenharmony_ci	 * 14  1=rx req on FIFO half full
410562306a36Sopenharmony_ci	 * 13  tx data  IRQ enable
410662306a36Sopenharmony_ci	 * 12  tx idle  IRQ enable
410762306a36Sopenharmony_ci	 * 11  rx break on IRQ enable
410862306a36Sopenharmony_ci	 * 10  rx data  IRQ enable
410962306a36Sopenharmony_ci	 * 09  rx break off IRQ enable
411062306a36Sopenharmony_ci	 * 08  overrun  IRQ enable
411162306a36Sopenharmony_ci	 * 07  DSR      IRQ enable
411262306a36Sopenharmony_ci	 * 06  CTS      IRQ enable
411362306a36Sopenharmony_ci	 * 05  DCD      IRQ enable
411462306a36Sopenharmony_ci	 * 04  RI       IRQ enable
411562306a36Sopenharmony_ci	 * 03  0=16x sampling, 1=8x sampling
411662306a36Sopenharmony_ci	 * 02  1=txd->rxd internal loopback enable
411762306a36Sopenharmony_ci	 * 01  reserved, must be zero
411862306a36Sopenharmony_ci	 * 00  1=master IRQ enable
411962306a36Sopenharmony_ci	 */
412062306a36Sopenharmony_ci	val = BIT15 + BIT14 + BIT0;
412162306a36Sopenharmony_ci	/* JCR[8] : 1 = x8 async mode feature available */
412262306a36Sopenharmony_ci	if ((rd_reg32(info, JCR) & BIT8) && info->params.data_rate &&
412362306a36Sopenharmony_ci	    ((info->base_clock < (info->params.data_rate * 16)) ||
412462306a36Sopenharmony_ci	     (info->base_clock % (info->params.data_rate * 16)))) {
412562306a36Sopenharmony_ci		/* use 8x sampling */
412662306a36Sopenharmony_ci		val |= BIT3;
412762306a36Sopenharmony_ci		set_rate(info, info->params.data_rate * 8);
412862306a36Sopenharmony_ci	} else {
412962306a36Sopenharmony_ci		/* use 16x sampling */
413062306a36Sopenharmony_ci		set_rate(info, info->params.data_rate * 16);
413162306a36Sopenharmony_ci	}
413262306a36Sopenharmony_ci	wr_reg16(info, SCR, val);
413362306a36Sopenharmony_ci
413462306a36Sopenharmony_ci	slgt_irq_on(info, IRQ_RXBREAK | IRQ_RXOVER);
413562306a36Sopenharmony_ci
413662306a36Sopenharmony_ci	if (info->params.loopback)
413762306a36Sopenharmony_ci		enable_loopback(info);
413862306a36Sopenharmony_ci}
413962306a36Sopenharmony_ci
414062306a36Sopenharmony_cistatic void sync_mode(struct slgt_info *info)
414162306a36Sopenharmony_ci{
414262306a36Sopenharmony_ci	unsigned short val;
414362306a36Sopenharmony_ci
414462306a36Sopenharmony_ci	slgt_irq_off(info, IRQ_ALL | IRQ_MASTER);
414562306a36Sopenharmony_ci	tx_stop(info);
414662306a36Sopenharmony_ci	rx_stop(info);
414762306a36Sopenharmony_ci
414862306a36Sopenharmony_ci	/* TCR (tx control)
414962306a36Sopenharmony_ci	 *
415062306a36Sopenharmony_ci	 * 15..13  mode
415162306a36Sopenharmony_ci	 *         000=HDLC/SDLC
415262306a36Sopenharmony_ci	 *         001=raw bit synchronous
415362306a36Sopenharmony_ci	 *         010=asynchronous/isochronous
415462306a36Sopenharmony_ci	 *         011=monosync byte synchronous
415562306a36Sopenharmony_ci	 *         100=bisync byte synchronous
415662306a36Sopenharmony_ci	 *         101=xsync byte synchronous
415762306a36Sopenharmony_ci	 * 12..10  encoding
415862306a36Sopenharmony_ci	 * 09      CRC enable
415962306a36Sopenharmony_ci	 * 08      CRC32
416062306a36Sopenharmony_ci	 * 07      1=RTS driver control
416162306a36Sopenharmony_ci	 * 06      preamble enable
416262306a36Sopenharmony_ci	 * 05..04  preamble length
416362306a36Sopenharmony_ci	 * 03      share open/close flag
416462306a36Sopenharmony_ci	 * 02      reset
416562306a36Sopenharmony_ci	 * 01      enable
416662306a36Sopenharmony_ci	 * 00      auto-CTS enable
416762306a36Sopenharmony_ci	 */
416862306a36Sopenharmony_ci	val = BIT2;
416962306a36Sopenharmony_ci
417062306a36Sopenharmony_ci	switch(info->params.mode) {
417162306a36Sopenharmony_ci	case MGSL_MODE_XSYNC:
417262306a36Sopenharmony_ci		val |= BIT15 + BIT13;
417362306a36Sopenharmony_ci		break;
417462306a36Sopenharmony_ci	case MGSL_MODE_MONOSYNC: val |= BIT14 + BIT13; break;
417562306a36Sopenharmony_ci	case MGSL_MODE_BISYNC:   val |= BIT15; break;
417662306a36Sopenharmony_ci	case MGSL_MODE_RAW:      val |= BIT13; break;
417762306a36Sopenharmony_ci	}
417862306a36Sopenharmony_ci	if (info->if_mode & MGSL_INTERFACE_RTS_EN)
417962306a36Sopenharmony_ci		val |= BIT7;
418062306a36Sopenharmony_ci
418162306a36Sopenharmony_ci	switch(info->params.encoding)
418262306a36Sopenharmony_ci	{
418362306a36Sopenharmony_ci	case HDLC_ENCODING_NRZB:          val |= BIT10; break;
418462306a36Sopenharmony_ci	case HDLC_ENCODING_NRZI_MARK:     val |= BIT11; break;
418562306a36Sopenharmony_ci	case HDLC_ENCODING_NRZI:          val |= BIT11 + BIT10; break;
418662306a36Sopenharmony_ci	case HDLC_ENCODING_BIPHASE_MARK:  val |= BIT12; break;
418762306a36Sopenharmony_ci	case HDLC_ENCODING_BIPHASE_SPACE: val |= BIT12 + BIT10; break;
418862306a36Sopenharmony_ci	case HDLC_ENCODING_BIPHASE_LEVEL: val |= BIT12 + BIT11; break;
418962306a36Sopenharmony_ci	case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: val |= BIT12 + BIT11 + BIT10; break;
419062306a36Sopenharmony_ci	}
419162306a36Sopenharmony_ci
419262306a36Sopenharmony_ci	switch (info->params.crc_type & HDLC_CRC_MASK)
419362306a36Sopenharmony_ci	{
419462306a36Sopenharmony_ci	case HDLC_CRC_16_CCITT: val |= BIT9; break;
419562306a36Sopenharmony_ci	case HDLC_CRC_32_CCITT: val |= BIT9 + BIT8; break;
419662306a36Sopenharmony_ci	}
419762306a36Sopenharmony_ci
419862306a36Sopenharmony_ci	if (info->params.preamble != HDLC_PREAMBLE_PATTERN_NONE)
419962306a36Sopenharmony_ci		val |= BIT6;
420062306a36Sopenharmony_ci
420162306a36Sopenharmony_ci	switch (info->params.preamble_length)
420262306a36Sopenharmony_ci	{
420362306a36Sopenharmony_ci	case HDLC_PREAMBLE_LENGTH_16BITS: val |= BIT5; break;
420462306a36Sopenharmony_ci	case HDLC_PREAMBLE_LENGTH_32BITS: val |= BIT4; break;
420562306a36Sopenharmony_ci	case HDLC_PREAMBLE_LENGTH_64BITS: val |= BIT5 + BIT4; break;
420662306a36Sopenharmony_ci	}
420762306a36Sopenharmony_ci
420862306a36Sopenharmony_ci	if (info->params.flags & HDLC_FLAG_AUTO_CTS)
420962306a36Sopenharmony_ci		val |= BIT0;
421062306a36Sopenharmony_ci
421162306a36Sopenharmony_ci	wr_reg16(info, TCR, val);
421262306a36Sopenharmony_ci
421362306a36Sopenharmony_ci	/* TPR (transmit preamble) */
421462306a36Sopenharmony_ci
421562306a36Sopenharmony_ci	switch (info->params.preamble)
421662306a36Sopenharmony_ci	{
421762306a36Sopenharmony_ci	case HDLC_PREAMBLE_PATTERN_FLAGS: val = 0x7e; break;
421862306a36Sopenharmony_ci	case HDLC_PREAMBLE_PATTERN_ONES:  val = 0xff; break;
421962306a36Sopenharmony_ci	case HDLC_PREAMBLE_PATTERN_ZEROS: val = 0x00; break;
422062306a36Sopenharmony_ci	case HDLC_PREAMBLE_PATTERN_10:    val = 0x55; break;
422162306a36Sopenharmony_ci	case HDLC_PREAMBLE_PATTERN_01:    val = 0xaa; break;
422262306a36Sopenharmony_ci	default:                          val = 0x7e; break;
422362306a36Sopenharmony_ci	}
422462306a36Sopenharmony_ci	wr_reg8(info, TPR, (unsigned char)val);
422562306a36Sopenharmony_ci
422662306a36Sopenharmony_ci	/* RCR (rx control)
422762306a36Sopenharmony_ci	 *
422862306a36Sopenharmony_ci	 * 15..13  mode
422962306a36Sopenharmony_ci	 *         000=HDLC/SDLC
423062306a36Sopenharmony_ci	 *         001=raw bit synchronous
423162306a36Sopenharmony_ci	 *         010=asynchronous/isochronous
423262306a36Sopenharmony_ci	 *         011=monosync byte synchronous
423362306a36Sopenharmony_ci	 *         100=bisync byte synchronous
423462306a36Sopenharmony_ci	 *         101=xsync byte synchronous
423562306a36Sopenharmony_ci	 * 12..10  encoding
423662306a36Sopenharmony_ci	 * 09      CRC enable
423762306a36Sopenharmony_ci	 * 08      CRC32
423862306a36Sopenharmony_ci	 * 07..03  reserved, must be 0
423962306a36Sopenharmony_ci	 * 02      reset
424062306a36Sopenharmony_ci	 * 01      enable
424162306a36Sopenharmony_ci	 * 00      auto-DCD enable
424262306a36Sopenharmony_ci	 */
424362306a36Sopenharmony_ci	val = 0;
424462306a36Sopenharmony_ci
424562306a36Sopenharmony_ci	switch(info->params.mode) {
424662306a36Sopenharmony_ci	case MGSL_MODE_XSYNC:
424762306a36Sopenharmony_ci		val |= BIT15 + BIT13;
424862306a36Sopenharmony_ci		break;
424962306a36Sopenharmony_ci	case MGSL_MODE_MONOSYNC: val |= BIT14 + BIT13; break;
425062306a36Sopenharmony_ci	case MGSL_MODE_BISYNC:   val |= BIT15; break;
425162306a36Sopenharmony_ci	case MGSL_MODE_RAW:      val |= BIT13; break;
425262306a36Sopenharmony_ci	}
425362306a36Sopenharmony_ci
425462306a36Sopenharmony_ci	switch(info->params.encoding)
425562306a36Sopenharmony_ci	{
425662306a36Sopenharmony_ci	case HDLC_ENCODING_NRZB:          val |= BIT10; break;
425762306a36Sopenharmony_ci	case HDLC_ENCODING_NRZI_MARK:     val |= BIT11; break;
425862306a36Sopenharmony_ci	case HDLC_ENCODING_NRZI:          val |= BIT11 + BIT10; break;
425962306a36Sopenharmony_ci	case HDLC_ENCODING_BIPHASE_MARK:  val |= BIT12; break;
426062306a36Sopenharmony_ci	case HDLC_ENCODING_BIPHASE_SPACE: val |= BIT12 + BIT10; break;
426162306a36Sopenharmony_ci	case HDLC_ENCODING_BIPHASE_LEVEL: val |= BIT12 + BIT11; break;
426262306a36Sopenharmony_ci	case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: val |= BIT12 + BIT11 + BIT10; break;
426362306a36Sopenharmony_ci	}
426462306a36Sopenharmony_ci
426562306a36Sopenharmony_ci	switch (info->params.crc_type & HDLC_CRC_MASK)
426662306a36Sopenharmony_ci	{
426762306a36Sopenharmony_ci	case HDLC_CRC_16_CCITT: val |= BIT9; break;
426862306a36Sopenharmony_ci	case HDLC_CRC_32_CCITT: val |= BIT9 + BIT8; break;
426962306a36Sopenharmony_ci	}
427062306a36Sopenharmony_ci
427162306a36Sopenharmony_ci	if (info->params.flags & HDLC_FLAG_AUTO_DCD)
427262306a36Sopenharmony_ci		val |= BIT0;
427362306a36Sopenharmony_ci
427462306a36Sopenharmony_ci	wr_reg16(info, RCR, val);
427562306a36Sopenharmony_ci
427662306a36Sopenharmony_ci	/* CCR (clock control)
427762306a36Sopenharmony_ci	 *
427862306a36Sopenharmony_ci	 * 07..05  tx clock source
427962306a36Sopenharmony_ci	 * 04..02  rx clock source
428062306a36Sopenharmony_ci	 * 01      auxclk enable
428162306a36Sopenharmony_ci	 * 00      BRG enable
428262306a36Sopenharmony_ci	 */
428362306a36Sopenharmony_ci	val = 0;
428462306a36Sopenharmony_ci
428562306a36Sopenharmony_ci	if (info->params.flags & HDLC_FLAG_TXC_BRG)
428662306a36Sopenharmony_ci	{
428762306a36Sopenharmony_ci		// when RxC source is DPLL, BRG generates 16X DPLL
428862306a36Sopenharmony_ci		// reference clock, so take TxC from BRG/16 to get
428962306a36Sopenharmony_ci		// transmit clock at actual data rate
429062306a36Sopenharmony_ci		if (info->params.flags & HDLC_FLAG_RXC_DPLL)
429162306a36Sopenharmony_ci			val |= BIT6 + BIT5;	/* 011, txclk = BRG/16 */
429262306a36Sopenharmony_ci		else
429362306a36Sopenharmony_ci			val |= BIT6;	/* 010, txclk = BRG */
429462306a36Sopenharmony_ci	}
429562306a36Sopenharmony_ci	else if (info->params.flags & HDLC_FLAG_TXC_DPLL)
429662306a36Sopenharmony_ci		val |= BIT7;	/* 100, txclk = DPLL Input */
429762306a36Sopenharmony_ci	else if (info->params.flags & HDLC_FLAG_TXC_RXCPIN)
429862306a36Sopenharmony_ci		val |= BIT5;	/* 001, txclk = RXC Input */
429962306a36Sopenharmony_ci
430062306a36Sopenharmony_ci	if (info->params.flags & HDLC_FLAG_RXC_BRG)
430162306a36Sopenharmony_ci		val |= BIT3;	/* 010, rxclk = BRG */
430262306a36Sopenharmony_ci	else if (info->params.flags & HDLC_FLAG_RXC_DPLL)
430362306a36Sopenharmony_ci		val |= BIT4;	/* 100, rxclk = DPLL */
430462306a36Sopenharmony_ci	else if (info->params.flags & HDLC_FLAG_RXC_TXCPIN)
430562306a36Sopenharmony_ci		val |= BIT2;	/* 001, rxclk = TXC Input */
430662306a36Sopenharmony_ci
430762306a36Sopenharmony_ci	if (info->params.clock_speed)
430862306a36Sopenharmony_ci		val |= BIT1 + BIT0;
430962306a36Sopenharmony_ci
431062306a36Sopenharmony_ci	wr_reg8(info, CCR, (unsigned char)val);
431162306a36Sopenharmony_ci
431262306a36Sopenharmony_ci	if (info->params.flags & (HDLC_FLAG_TXC_DPLL + HDLC_FLAG_RXC_DPLL))
431362306a36Sopenharmony_ci	{
431462306a36Sopenharmony_ci		// program DPLL mode
431562306a36Sopenharmony_ci		switch(info->params.encoding)
431662306a36Sopenharmony_ci		{
431762306a36Sopenharmony_ci		case HDLC_ENCODING_BIPHASE_MARK:
431862306a36Sopenharmony_ci		case HDLC_ENCODING_BIPHASE_SPACE:
431962306a36Sopenharmony_ci			val = BIT7; break;
432062306a36Sopenharmony_ci		case HDLC_ENCODING_BIPHASE_LEVEL:
432162306a36Sopenharmony_ci		case HDLC_ENCODING_DIFF_BIPHASE_LEVEL:
432262306a36Sopenharmony_ci			val = BIT7 + BIT6; break;
432362306a36Sopenharmony_ci		default: val = BIT6;	// NRZ encodings
432462306a36Sopenharmony_ci		}
432562306a36Sopenharmony_ci		wr_reg16(info, RCR, (unsigned short)(rd_reg16(info, RCR) | val));
432662306a36Sopenharmony_ci
432762306a36Sopenharmony_ci		// DPLL requires a 16X reference clock from BRG
432862306a36Sopenharmony_ci		set_rate(info, info->params.clock_speed * 16);
432962306a36Sopenharmony_ci	}
433062306a36Sopenharmony_ci	else
433162306a36Sopenharmony_ci		set_rate(info, info->params.clock_speed);
433262306a36Sopenharmony_ci
433362306a36Sopenharmony_ci	tx_set_idle(info);
433462306a36Sopenharmony_ci
433562306a36Sopenharmony_ci	msc_set_vcr(info);
433662306a36Sopenharmony_ci
433762306a36Sopenharmony_ci	/* SCR (serial control)
433862306a36Sopenharmony_ci	 *
433962306a36Sopenharmony_ci	 * 15  1=tx req on FIFO half empty
434062306a36Sopenharmony_ci	 * 14  1=rx req on FIFO half full
434162306a36Sopenharmony_ci	 * 13  tx data  IRQ enable
434262306a36Sopenharmony_ci	 * 12  tx idle  IRQ enable
434362306a36Sopenharmony_ci	 * 11  underrun IRQ enable
434462306a36Sopenharmony_ci	 * 10  rx data  IRQ enable
434562306a36Sopenharmony_ci	 * 09  rx idle  IRQ enable
434662306a36Sopenharmony_ci	 * 08  overrun  IRQ enable
434762306a36Sopenharmony_ci	 * 07  DSR      IRQ enable
434862306a36Sopenharmony_ci	 * 06  CTS      IRQ enable
434962306a36Sopenharmony_ci	 * 05  DCD      IRQ enable
435062306a36Sopenharmony_ci	 * 04  RI       IRQ enable
435162306a36Sopenharmony_ci	 * 03  reserved, must be zero
435262306a36Sopenharmony_ci	 * 02  1=txd->rxd internal loopback enable
435362306a36Sopenharmony_ci	 * 01  reserved, must be zero
435462306a36Sopenharmony_ci	 * 00  1=master IRQ enable
435562306a36Sopenharmony_ci	 */
435662306a36Sopenharmony_ci	wr_reg16(info, SCR, BIT15 + BIT14 + BIT0);
435762306a36Sopenharmony_ci
435862306a36Sopenharmony_ci	if (info->params.loopback)
435962306a36Sopenharmony_ci		enable_loopback(info);
436062306a36Sopenharmony_ci}
436162306a36Sopenharmony_ci
436262306a36Sopenharmony_ci/*
436362306a36Sopenharmony_ci *  set transmit idle mode
436462306a36Sopenharmony_ci */
436562306a36Sopenharmony_cistatic void tx_set_idle(struct slgt_info *info)
436662306a36Sopenharmony_ci{
436762306a36Sopenharmony_ci	unsigned char val;
436862306a36Sopenharmony_ci	unsigned short tcr;
436962306a36Sopenharmony_ci
437062306a36Sopenharmony_ci	/* if preamble enabled (tcr[6] == 1) then tx idle size = 8 bits
437162306a36Sopenharmony_ci	 * else tcr[5:4] = tx idle size: 00 = 8 bits, 01 = 16 bits
437262306a36Sopenharmony_ci	 */
437362306a36Sopenharmony_ci	tcr = rd_reg16(info, TCR);
437462306a36Sopenharmony_ci	if (info->idle_mode & HDLC_TXIDLE_CUSTOM_16) {
437562306a36Sopenharmony_ci		/* disable preamble, set idle size to 16 bits */
437662306a36Sopenharmony_ci		tcr = (tcr & ~(BIT6 + BIT5)) | BIT4;
437762306a36Sopenharmony_ci		/* MSB of 16 bit idle specified in tx preamble register (TPR) */
437862306a36Sopenharmony_ci		wr_reg8(info, TPR, (unsigned char)((info->idle_mode >> 8) & 0xff));
437962306a36Sopenharmony_ci	} else if (!(tcr & BIT6)) {
438062306a36Sopenharmony_ci		/* preamble is disabled, set idle size to 8 bits */
438162306a36Sopenharmony_ci		tcr &= ~(BIT5 + BIT4);
438262306a36Sopenharmony_ci	}
438362306a36Sopenharmony_ci	wr_reg16(info, TCR, tcr);
438462306a36Sopenharmony_ci
438562306a36Sopenharmony_ci	if (info->idle_mode & (HDLC_TXIDLE_CUSTOM_8 | HDLC_TXIDLE_CUSTOM_16)) {
438662306a36Sopenharmony_ci		/* LSB of custom tx idle specified in tx idle register */
438762306a36Sopenharmony_ci		val = (unsigned char)(info->idle_mode & 0xff);
438862306a36Sopenharmony_ci	} else {
438962306a36Sopenharmony_ci		/* standard 8 bit idle patterns */
439062306a36Sopenharmony_ci		switch(info->idle_mode)
439162306a36Sopenharmony_ci		{
439262306a36Sopenharmony_ci		case HDLC_TXIDLE_FLAGS:          val = 0x7e; break;
439362306a36Sopenharmony_ci		case HDLC_TXIDLE_ALT_ZEROS_ONES:
439462306a36Sopenharmony_ci		case HDLC_TXIDLE_ALT_MARK_SPACE: val = 0xaa; break;
439562306a36Sopenharmony_ci		case HDLC_TXIDLE_ZEROS:
439662306a36Sopenharmony_ci		case HDLC_TXIDLE_SPACE:          val = 0x00; break;
439762306a36Sopenharmony_ci		default:                         val = 0xff;
439862306a36Sopenharmony_ci		}
439962306a36Sopenharmony_ci	}
440062306a36Sopenharmony_ci
440162306a36Sopenharmony_ci	wr_reg8(info, TIR, val);
440262306a36Sopenharmony_ci}
440362306a36Sopenharmony_ci
440462306a36Sopenharmony_ci/*
440562306a36Sopenharmony_ci * get state of V24 status (input) signals
440662306a36Sopenharmony_ci */
440762306a36Sopenharmony_cistatic void get_gtsignals(struct slgt_info *info)
440862306a36Sopenharmony_ci{
440962306a36Sopenharmony_ci	unsigned short status = rd_reg16(info, SSR);
441062306a36Sopenharmony_ci
441162306a36Sopenharmony_ci	/* clear all serial signals except RTS and DTR */
441262306a36Sopenharmony_ci	info->signals &= SerialSignal_RTS | SerialSignal_DTR;
441362306a36Sopenharmony_ci
441462306a36Sopenharmony_ci	if (status & BIT3)
441562306a36Sopenharmony_ci		info->signals |= SerialSignal_DSR;
441662306a36Sopenharmony_ci	if (status & BIT2)
441762306a36Sopenharmony_ci		info->signals |= SerialSignal_CTS;
441862306a36Sopenharmony_ci	if (status & BIT1)
441962306a36Sopenharmony_ci		info->signals |= SerialSignal_DCD;
442062306a36Sopenharmony_ci	if (status & BIT0)
442162306a36Sopenharmony_ci		info->signals |= SerialSignal_RI;
442262306a36Sopenharmony_ci}
442362306a36Sopenharmony_ci
442462306a36Sopenharmony_ci/*
442562306a36Sopenharmony_ci * set V.24 Control Register based on current configuration
442662306a36Sopenharmony_ci */
442762306a36Sopenharmony_cistatic void msc_set_vcr(struct slgt_info *info)
442862306a36Sopenharmony_ci{
442962306a36Sopenharmony_ci	unsigned char val = 0;
443062306a36Sopenharmony_ci
443162306a36Sopenharmony_ci	/* VCR (V.24 control)
443262306a36Sopenharmony_ci	 *
443362306a36Sopenharmony_ci	 * 07..04  serial IF select
443462306a36Sopenharmony_ci	 * 03      DTR
443562306a36Sopenharmony_ci	 * 02      RTS
443662306a36Sopenharmony_ci	 * 01      LL
443762306a36Sopenharmony_ci	 * 00      RL
443862306a36Sopenharmony_ci	 */
443962306a36Sopenharmony_ci
444062306a36Sopenharmony_ci	switch(info->if_mode & MGSL_INTERFACE_MASK)
444162306a36Sopenharmony_ci	{
444262306a36Sopenharmony_ci	case MGSL_INTERFACE_RS232:
444362306a36Sopenharmony_ci		val |= BIT5; /* 0010 */
444462306a36Sopenharmony_ci		break;
444562306a36Sopenharmony_ci	case MGSL_INTERFACE_V35:
444662306a36Sopenharmony_ci		val |= BIT7 + BIT6 + BIT5; /* 1110 */
444762306a36Sopenharmony_ci		break;
444862306a36Sopenharmony_ci	case MGSL_INTERFACE_RS422:
444962306a36Sopenharmony_ci		val |= BIT6; /* 0100 */
445062306a36Sopenharmony_ci		break;
445162306a36Sopenharmony_ci	}
445262306a36Sopenharmony_ci
445362306a36Sopenharmony_ci	if (info->if_mode & MGSL_INTERFACE_MSB_FIRST)
445462306a36Sopenharmony_ci		val |= BIT4;
445562306a36Sopenharmony_ci	if (info->signals & SerialSignal_DTR)
445662306a36Sopenharmony_ci		val |= BIT3;
445762306a36Sopenharmony_ci	if (info->signals & SerialSignal_RTS)
445862306a36Sopenharmony_ci		val |= BIT2;
445962306a36Sopenharmony_ci	if (info->if_mode & MGSL_INTERFACE_LL)
446062306a36Sopenharmony_ci		val |= BIT1;
446162306a36Sopenharmony_ci	if (info->if_mode & MGSL_INTERFACE_RL)
446262306a36Sopenharmony_ci		val |= BIT0;
446362306a36Sopenharmony_ci	wr_reg8(info, VCR, val);
446462306a36Sopenharmony_ci}
446562306a36Sopenharmony_ci
446662306a36Sopenharmony_ci/*
446762306a36Sopenharmony_ci * set state of V24 control (output) signals
446862306a36Sopenharmony_ci */
446962306a36Sopenharmony_cistatic void set_gtsignals(struct slgt_info *info)
447062306a36Sopenharmony_ci{
447162306a36Sopenharmony_ci	unsigned char val = rd_reg8(info, VCR);
447262306a36Sopenharmony_ci	if (info->signals & SerialSignal_DTR)
447362306a36Sopenharmony_ci		val |= BIT3;
447462306a36Sopenharmony_ci	else
447562306a36Sopenharmony_ci		val &= ~BIT3;
447662306a36Sopenharmony_ci	if (info->signals & SerialSignal_RTS)
447762306a36Sopenharmony_ci		val |= BIT2;
447862306a36Sopenharmony_ci	else
447962306a36Sopenharmony_ci		val &= ~BIT2;
448062306a36Sopenharmony_ci	wr_reg8(info, VCR, val);
448162306a36Sopenharmony_ci}
448262306a36Sopenharmony_ci
448362306a36Sopenharmony_ci/*
448462306a36Sopenharmony_ci * free range of receive DMA buffers (i to last)
448562306a36Sopenharmony_ci */
448662306a36Sopenharmony_cistatic void free_rbufs(struct slgt_info *info, unsigned int i, unsigned int last)
448762306a36Sopenharmony_ci{
448862306a36Sopenharmony_ci	int done = 0;
448962306a36Sopenharmony_ci
449062306a36Sopenharmony_ci	while(!done) {
449162306a36Sopenharmony_ci		/* reset current buffer for reuse */
449262306a36Sopenharmony_ci		info->rbufs[i].status = 0;
449362306a36Sopenharmony_ci		set_desc_count(info->rbufs[i], info->rbuf_fill_level);
449462306a36Sopenharmony_ci		if (i == last)
449562306a36Sopenharmony_ci			done = 1;
449662306a36Sopenharmony_ci		if (++i == info->rbuf_count)
449762306a36Sopenharmony_ci			i = 0;
449862306a36Sopenharmony_ci	}
449962306a36Sopenharmony_ci	info->rbuf_current = i;
450062306a36Sopenharmony_ci}
450162306a36Sopenharmony_ci
450262306a36Sopenharmony_ci/*
450362306a36Sopenharmony_ci * mark all receive DMA buffers as free
450462306a36Sopenharmony_ci */
450562306a36Sopenharmony_cistatic void reset_rbufs(struct slgt_info *info)
450662306a36Sopenharmony_ci{
450762306a36Sopenharmony_ci	free_rbufs(info, 0, info->rbuf_count - 1);
450862306a36Sopenharmony_ci	info->rbuf_fill_index = 0;
450962306a36Sopenharmony_ci	info->rbuf_fill_count = 0;
451062306a36Sopenharmony_ci}
451162306a36Sopenharmony_ci
451262306a36Sopenharmony_ci/*
451362306a36Sopenharmony_ci * pass receive HDLC frame to upper layer
451462306a36Sopenharmony_ci *
451562306a36Sopenharmony_ci * return true if frame available, otherwise false
451662306a36Sopenharmony_ci */
451762306a36Sopenharmony_cistatic bool rx_get_frame(struct slgt_info *info)
451862306a36Sopenharmony_ci{
451962306a36Sopenharmony_ci	unsigned int start, end;
452062306a36Sopenharmony_ci	unsigned short status;
452162306a36Sopenharmony_ci	unsigned int framesize = 0;
452262306a36Sopenharmony_ci	unsigned long flags;
452362306a36Sopenharmony_ci	struct tty_struct *tty = info->port.tty;
452462306a36Sopenharmony_ci	unsigned char addr_field = 0xff;
452562306a36Sopenharmony_ci	unsigned int crc_size = 0;
452662306a36Sopenharmony_ci
452762306a36Sopenharmony_ci	switch (info->params.crc_type & HDLC_CRC_MASK) {
452862306a36Sopenharmony_ci	case HDLC_CRC_16_CCITT: crc_size = 2; break;
452962306a36Sopenharmony_ci	case HDLC_CRC_32_CCITT: crc_size = 4; break;
453062306a36Sopenharmony_ci	}
453162306a36Sopenharmony_ci
453262306a36Sopenharmony_cicheck_again:
453362306a36Sopenharmony_ci
453462306a36Sopenharmony_ci	framesize = 0;
453562306a36Sopenharmony_ci	addr_field = 0xff;
453662306a36Sopenharmony_ci	start = end = info->rbuf_current;
453762306a36Sopenharmony_ci
453862306a36Sopenharmony_ci	for (;;) {
453962306a36Sopenharmony_ci		if (!desc_complete(info->rbufs[end]))
454062306a36Sopenharmony_ci			goto cleanup;
454162306a36Sopenharmony_ci
454262306a36Sopenharmony_ci		if (framesize == 0 && info->params.addr_filter != 0xff)
454362306a36Sopenharmony_ci			addr_field = info->rbufs[end].buf[0];
454462306a36Sopenharmony_ci
454562306a36Sopenharmony_ci		framesize += desc_count(info->rbufs[end]);
454662306a36Sopenharmony_ci
454762306a36Sopenharmony_ci		if (desc_eof(info->rbufs[end]))
454862306a36Sopenharmony_ci			break;
454962306a36Sopenharmony_ci
455062306a36Sopenharmony_ci		if (++end == info->rbuf_count)
455162306a36Sopenharmony_ci			end = 0;
455262306a36Sopenharmony_ci
455362306a36Sopenharmony_ci		if (end == info->rbuf_current) {
455462306a36Sopenharmony_ci			if (info->rx_enabled){
455562306a36Sopenharmony_ci				spin_lock_irqsave(&info->lock,flags);
455662306a36Sopenharmony_ci				rx_start(info);
455762306a36Sopenharmony_ci				spin_unlock_irqrestore(&info->lock,flags);
455862306a36Sopenharmony_ci			}
455962306a36Sopenharmony_ci			goto cleanup;
456062306a36Sopenharmony_ci		}
456162306a36Sopenharmony_ci	}
456262306a36Sopenharmony_ci
456362306a36Sopenharmony_ci	/* status
456462306a36Sopenharmony_ci	 *
456562306a36Sopenharmony_ci	 * 15      buffer complete
456662306a36Sopenharmony_ci	 * 14..06  reserved
456762306a36Sopenharmony_ci	 * 05..04  residue
456862306a36Sopenharmony_ci	 * 02      eof (end of frame)
456962306a36Sopenharmony_ci	 * 01      CRC error
457062306a36Sopenharmony_ci	 * 00      abort
457162306a36Sopenharmony_ci	 */
457262306a36Sopenharmony_ci	status = desc_status(info->rbufs[end]);
457362306a36Sopenharmony_ci
457462306a36Sopenharmony_ci	/* ignore CRC bit if not using CRC (bit is undefined) */
457562306a36Sopenharmony_ci	if ((info->params.crc_type & HDLC_CRC_MASK) == HDLC_CRC_NONE)
457662306a36Sopenharmony_ci		status &= ~BIT1;
457762306a36Sopenharmony_ci
457862306a36Sopenharmony_ci	if (framesize == 0 ||
457962306a36Sopenharmony_ci		 (addr_field != 0xff && addr_field != info->params.addr_filter)) {
458062306a36Sopenharmony_ci		free_rbufs(info, start, end);
458162306a36Sopenharmony_ci		goto check_again;
458262306a36Sopenharmony_ci	}
458362306a36Sopenharmony_ci
458462306a36Sopenharmony_ci	if (framesize < (2 + crc_size) || status & BIT0) {
458562306a36Sopenharmony_ci		info->icount.rxshort++;
458662306a36Sopenharmony_ci		framesize = 0;
458762306a36Sopenharmony_ci	} else if (status & BIT1) {
458862306a36Sopenharmony_ci		info->icount.rxcrc++;
458962306a36Sopenharmony_ci		if (!(info->params.crc_type & HDLC_CRC_RETURN_EX))
459062306a36Sopenharmony_ci			framesize = 0;
459162306a36Sopenharmony_ci	}
459262306a36Sopenharmony_ci
459362306a36Sopenharmony_ci#if SYNCLINK_GENERIC_HDLC
459462306a36Sopenharmony_ci	if (framesize == 0) {
459562306a36Sopenharmony_ci		info->netdev->stats.rx_errors++;
459662306a36Sopenharmony_ci		info->netdev->stats.rx_frame_errors++;
459762306a36Sopenharmony_ci	}
459862306a36Sopenharmony_ci#endif
459962306a36Sopenharmony_ci
460062306a36Sopenharmony_ci	DBGBH(("%s rx frame status=%04X size=%d\n",
460162306a36Sopenharmony_ci		info->device_name, status, framesize));
460262306a36Sopenharmony_ci	DBGDATA(info, info->rbufs[start].buf, min_t(int, framesize, info->rbuf_fill_level), "rx");
460362306a36Sopenharmony_ci
460462306a36Sopenharmony_ci	if (framesize) {
460562306a36Sopenharmony_ci		if (!(info->params.crc_type & HDLC_CRC_RETURN_EX)) {
460662306a36Sopenharmony_ci			framesize -= crc_size;
460762306a36Sopenharmony_ci			crc_size = 0;
460862306a36Sopenharmony_ci		}
460962306a36Sopenharmony_ci
461062306a36Sopenharmony_ci		if (framesize > info->max_frame_size + crc_size)
461162306a36Sopenharmony_ci			info->icount.rxlong++;
461262306a36Sopenharmony_ci		else {
461362306a36Sopenharmony_ci			/* copy dma buffer(s) to contiguous temp buffer */
461462306a36Sopenharmony_ci			int copy_count = framesize;
461562306a36Sopenharmony_ci			int i = start;
461662306a36Sopenharmony_ci			unsigned char *p = info->tmp_rbuf;
461762306a36Sopenharmony_ci			info->tmp_rbuf_count = framesize;
461862306a36Sopenharmony_ci
461962306a36Sopenharmony_ci			info->icount.rxok++;
462062306a36Sopenharmony_ci
462162306a36Sopenharmony_ci			while(copy_count) {
462262306a36Sopenharmony_ci				int partial_count = min_t(int, copy_count, info->rbuf_fill_level);
462362306a36Sopenharmony_ci				memcpy(p, info->rbufs[i].buf, partial_count);
462462306a36Sopenharmony_ci				p += partial_count;
462562306a36Sopenharmony_ci				copy_count -= partial_count;
462662306a36Sopenharmony_ci				if (++i == info->rbuf_count)
462762306a36Sopenharmony_ci					i = 0;
462862306a36Sopenharmony_ci			}
462962306a36Sopenharmony_ci
463062306a36Sopenharmony_ci			if (info->params.crc_type & HDLC_CRC_RETURN_EX) {
463162306a36Sopenharmony_ci				*p = (status & BIT1) ? RX_CRC_ERROR : RX_OK;
463262306a36Sopenharmony_ci				framesize++;
463362306a36Sopenharmony_ci			}
463462306a36Sopenharmony_ci
463562306a36Sopenharmony_ci#if SYNCLINK_GENERIC_HDLC
463662306a36Sopenharmony_ci			if (info->netcount)
463762306a36Sopenharmony_ci				hdlcdev_rx(info,info->tmp_rbuf, framesize);
463862306a36Sopenharmony_ci			else
463962306a36Sopenharmony_ci#endif
464062306a36Sopenharmony_ci				ldisc_receive_buf(tty, info->tmp_rbuf, NULL,
464162306a36Sopenharmony_ci						  framesize);
464262306a36Sopenharmony_ci		}
464362306a36Sopenharmony_ci	}
464462306a36Sopenharmony_ci	free_rbufs(info, start, end);
464562306a36Sopenharmony_ci	return true;
464662306a36Sopenharmony_ci
464762306a36Sopenharmony_cicleanup:
464862306a36Sopenharmony_ci	return false;
464962306a36Sopenharmony_ci}
465062306a36Sopenharmony_ci
465162306a36Sopenharmony_ci/*
465262306a36Sopenharmony_ci * pass receive buffer (RAW synchronous mode) to tty layer
465362306a36Sopenharmony_ci * return true if buffer available, otherwise false
465462306a36Sopenharmony_ci */
465562306a36Sopenharmony_cistatic bool rx_get_buf(struct slgt_info *info)
465662306a36Sopenharmony_ci{
465762306a36Sopenharmony_ci	unsigned int i = info->rbuf_current;
465862306a36Sopenharmony_ci	unsigned int count;
465962306a36Sopenharmony_ci
466062306a36Sopenharmony_ci	if (!desc_complete(info->rbufs[i]))
466162306a36Sopenharmony_ci		return false;
466262306a36Sopenharmony_ci	count = desc_count(info->rbufs[i]);
466362306a36Sopenharmony_ci	switch(info->params.mode) {
466462306a36Sopenharmony_ci	case MGSL_MODE_MONOSYNC:
466562306a36Sopenharmony_ci	case MGSL_MODE_BISYNC:
466662306a36Sopenharmony_ci	case MGSL_MODE_XSYNC:
466762306a36Sopenharmony_ci		/* ignore residue in byte synchronous modes */
466862306a36Sopenharmony_ci		if (desc_residue(info->rbufs[i]))
466962306a36Sopenharmony_ci			count--;
467062306a36Sopenharmony_ci		break;
467162306a36Sopenharmony_ci	}
467262306a36Sopenharmony_ci	DBGDATA(info, info->rbufs[i].buf, count, "rx");
467362306a36Sopenharmony_ci	DBGINFO(("rx_get_buf size=%d\n", count));
467462306a36Sopenharmony_ci	if (count)
467562306a36Sopenharmony_ci		ldisc_receive_buf(info->port.tty, info->rbufs[i].buf, NULL,
467662306a36Sopenharmony_ci				  count);
467762306a36Sopenharmony_ci	free_rbufs(info, i, i);
467862306a36Sopenharmony_ci	return true;
467962306a36Sopenharmony_ci}
468062306a36Sopenharmony_ci
468162306a36Sopenharmony_cistatic void reset_tbufs(struct slgt_info *info)
468262306a36Sopenharmony_ci{
468362306a36Sopenharmony_ci	unsigned int i;
468462306a36Sopenharmony_ci	info->tbuf_current = 0;
468562306a36Sopenharmony_ci	for (i=0 ; i < info->tbuf_count ; i++) {
468662306a36Sopenharmony_ci		info->tbufs[i].status = 0;
468762306a36Sopenharmony_ci		info->tbufs[i].count  = 0;
468862306a36Sopenharmony_ci	}
468962306a36Sopenharmony_ci}
469062306a36Sopenharmony_ci
469162306a36Sopenharmony_ci/*
469262306a36Sopenharmony_ci * return number of free transmit DMA buffers
469362306a36Sopenharmony_ci */
469462306a36Sopenharmony_cistatic unsigned int free_tbuf_count(struct slgt_info *info)
469562306a36Sopenharmony_ci{
469662306a36Sopenharmony_ci	unsigned int count = 0;
469762306a36Sopenharmony_ci	unsigned int i = info->tbuf_current;
469862306a36Sopenharmony_ci
469962306a36Sopenharmony_ci	do
470062306a36Sopenharmony_ci	{
470162306a36Sopenharmony_ci		if (desc_count(info->tbufs[i]))
470262306a36Sopenharmony_ci			break; /* buffer in use */
470362306a36Sopenharmony_ci		++count;
470462306a36Sopenharmony_ci		if (++i == info->tbuf_count)
470562306a36Sopenharmony_ci			i=0;
470662306a36Sopenharmony_ci	} while (i != info->tbuf_current);
470762306a36Sopenharmony_ci
470862306a36Sopenharmony_ci	/* if tx DMA active, last zero count buffer is in use */
470962306a36Sopenharmony_ci	if (count && (rd_reg32(info, TDCSR) & BIT0))
471062306a36Sopenharmony_ci		--count;
471162306a36Sopenharmony_ci
471262306a36Sopenharmony_ci	return count;
471362306a36Sopenharmony_ci}
471462306a36Sopenharmony_ci
471562306a36Sopenharmony_ci/*
471662306a36Sopenharmony_ci * return number of bytes in unsent transmit DMA buffers
471762306a36Sopenharmony_ci * and the serial controller tx FIFO
471862306a36Sopenharmony_ci */
471962306a36Sopenharmony_cistatic unsigned int tbuf_bytes(struct slgt_info *info)
472062306a36Sopenharmony_ci{
472162306a36Sopenharmony_ci	unsigned int total_count = 0;
472262306a36Sopenharmony_ci	unsigned int i = info->tbuf_current;
472362306a36Sopenharmony_ci	unsigned int reg_value;
472462306a36Sopenharmony_ci	unsigned int count;
472562306a36Sopenharmony_ci	unsigned int active_buf_count = 0;
472662306a36Sopenharmony_ci
472762306a36Sopenharmony_ci	/*
472862306a36Sopenharmony_ci	 * Add descriptor counts for all tx DMA buffers.
472962306a36Sopenharmony_ci	 * If count is zero (cleared by DMA controller after read),
473062306a36Sopenharmony_ci	 * the buffer is complete or is actively being read from.
473162306a36Sopenharmony_ci	 *
473262306a36Sopenharmony_ci	 * Record buf_count of last buffer with zero count starting
473362306a36Sopenharmony_ci	 * from current ring position. buf_count is mirror
473462306a36Sopenharmony_ci	 * copy of count and is not cleared by serial controller.
473562306a36Sopenharmony_ci	 * If DMA controller is active, that buffer is actively
473662306a36Sopenharmony_ci	 * being read so add to total.
473762306a36Sopenharmony_ci	 */
473862306a36Sopenharmony_ci	do {
473962306a36Sopenharmony_ci		count = desc_count(info->tbufs[i]);
474062306a36Sopenharmony_ci		if (count)
474162306a36Sopenharmony_ci			total_count += count;
474262306a36Sopenharmony_ci		else if (!total_count)
474362306a36Sopenharmony_ci			active_buf_count = info->tbufs[i].buf_count;
474462306a36Sopenharmony_ci		if (++i == info->tbuf_count)
474562306a36Sopenharmony_ci			i = 0;
474662306a36Sopenharmony_ci	} while (i != info->tbuf_current);
474762306a36Sopenharmony_ci
474862306a36Sopenharmony_ci	/* read tx DMA status register */
474962306a36Sopenharmony_ci	reg_value = rd_reg32(info, TDCSR);
475062306a36Sopenharmony_ci
475162306a36Sopenharmony_ci	/* if tx DMA active, last zero count buffer is in use */
475262306a36Sopenharmony_ci	if (reg_value & BIT0)
475362306a36Sopenharmony_ci		total_count += active_buf_count;
475462306a36Sopenharmony_ci
475562306a36Sopenharmony_ci	/* add tx FIFO count = reg_value[15..8] */
475662306a36Sopenharmony_ci	total_count += (reg_value >> 8) & 0xff;
475762306a36Sopenharmony_ci
475862306a36Sopenharmony_ci	/* if transmitter active add one byte for shift register */
475962306a36Sopenharmony_ci	if (info->tx_active)
476062306a36Sopenharmony_ci		total_count++;
476162306a36Sopenharmony_ci
476262306a36Sopenharmony_ci	return total_count;
476362306a36Sopenharmony_ci}
476462306a36Sopenharmony_ci
476562306a36Sopenharmony_ci/*
476662306a36Sopenharmony_ci * load data into transmit DMA buffer ring and start transmitter if needed
476762306a36Sopenharmony_ci * return true if data accepted, otherwise false (buffers full)
476862306a36Sopenharmony_ci */
476962306a36Sopenharmony_cistatic bool tx_load(struct slgt_info *info, const u8 *buf, unsigned int size)
477062306a36Sopenharmony_ci{
477162306a36Sopenharmony_ci	unsigned short count;
477262306a36Sopenharmony_ci	unsigned int i;
477362306a36Sopenharmony_ci	struct slgt_desc *d;
477462306a36Sopenharmony_ci
477562306a36Sopenharmony_ci	/* check required buffer space */
477662306a36Sopenharmony_ci	if (DIV_ROUND_UP(size, DMABUFSIZE) > free_tbuf_count(info))
477762306a36Sopenharmony_ci		return false;
477862306a36Sopenharmony_ci
477962306a36Sopenharmony_ci	DBGDATA(info, buf, size, "tx");
478062306a36Sopenharmony_ci
478162306a36Sopenharmony_ci	/*
478262306a36Sopenharmony_ci	 * copy data to one or more DMA buffers in circular ring
478362306a36Sopenharmony_ci	 * tbuf_start   = first buffer for this data
478462306a36Sopenharmony_ci	 * tbuf_current = next free buffer
478562306a36Sopenharmony_ci	 *
478662306a36Sopenharmony_ci	 * Copy all data before making data visible to DMA controller by
478762306a36Sopenharmony_ci	 * setting descriptor count of the first buffer.
478862306a36Sopenharmony_ci	 * This prevents an active DMA controller from reading the first DMA
478962306a36Sopenharmony_ci	 * buffers of a frame and stopping before the final buffers are filled.
479062306a36Sopenharmony_ci	 */
479162306a36Sopenharmony_ci
479262306a36Sopenharmony_ci	info->tbuf_start = i = info->tbuf_current;
479362306a36Sopenharmony_ci
479462306a36Sopenharmony_ci	while (size) {
479562306a36Sopenharmony_ci		d = &info->tbufs[i];
479662306a36Sopenharmony_ci
479762306a36Sopenharmony_ci		count = (unsigned short)((size > DMABUFSIZE) ? DMABUFSIZE : size);
479862306a36Sopenharmony_ci		memcpy(d->buf, buf, count);
479962306a36Sopenharmony_ci
480062306a36Sopenharmony_ci		size -= count;
480162306a36Sopenharmony_ci		buf  += count;
480262306a36Sopenharmony_ci
480362306a36Sopenharmony_ci		/*
480462306a36Sopenharmony_ci		 * set EOF bit for last buffer of HDLC frame or
480562306a36Sopenharmony_ci		 * for every buffer in raw mode
480662306a36Sopenharmony_ci		 */
480762306a36Sopenharmony_ci		if ((!size && info->params.mode == MGSL_MODE_HDLC) ||
480862306a36Sopenharmony_ci		    info->params.mode == MGSL_MODE_RAW)
480962306a36Sopenharmony_ci			set_desc_eof(*d, 1);
481062306a36Sopenharmony_ci		else
481162306a36Sopenharmony_ci			set_desc_eof(*d, 0);
481262306a36Sopenharmony_ci
481362306a36Sopenharmony_ci		/* set descriptor count for all but first buffer */
481462306a36Sopenharmony_ci		if (i != info->tbuf_start)
481562306a36Sopenharmony_ci			set_desc_count(*d, count);
481662306a36Sopenharmony_ci		d->buf_count = count;
481762306a36Sopenharmony_ci
481862306a36Sopenharmony_ci		if (++i == info->tbuf_count)
481962306a36Sopenharmony_ci			i = 0;
482062306a36Sopenharmony_ci	}
482162306a36Sopenharmony_ci
482262306a36Sopenharmony_ci	info->tbuf_current = i;
482362306a36Sopenharmony_ci
482462306a36Sopenharmony_ci	/* set first buffer count to make new data visible to DMA controller */
482562306a36Sopenharmony_ci	d = &info->tbufs[info->tbuf_start];
482662306a36Sopenharmony_ci	set_desc_count(*d, d->buf_count);
482762306a36Sopenharmony_ci
482862306a36Sopenharmony_ci	/* start transmitter if needed and update transmit timeout */
482962306a36Sopenharmony_ci	if (!info->tx_active)
483062306a36Sopenharmony_ci		tx_start(info);
483162306a36Sopenharmony_ci	update_tx_timer(info);
483262306a36Sopenharmony_ci
483362306a36Sopenharmony_ci	return true;
483462306a36Sopenharmony_ci}
483562306a36Sopenharmony_ci
483662306a36Sopenharmony_cistatic int register_test(struct slgt_info *info)
483762306a36Sopenharmony_ci{
483862306a36Sopenharmony_ci	static unsigned short patterns[] =
483962306a36Sopenharmony_ci		{0x0000, 0xffff, 0xaaaa, 0x5555, 0x6969, 0x9696};
484062306a36Sopenharmony_ci	static unsigned int count = ARRAY_SIZE(patterns);
484162306a36Sopenharmony_ci	unsigned int i;
484262306a36Sopenharmony_ci	int rc = 0;
484362306a36Sopenharmony_ci
484462306a36Sopenharmony_ci	for (i=0 ; i < count ; i++) {
484562306a36Sopenharmony_ci		wr_reg16(info, TIR, patterns[i]);
484662306a36Sopenharmony_ci		wr_reg16(info, BDR, patterns[(i+1)%count]);
484762306a36Sopenharmony_ci		if ((rd_reg16(info, TIR) != patterns[i]) ||
484862306a36Sopenharmony_ci		    (rd_reg16(info, BDR) != patterns[(i+1)%count])) {
484962306a36Sopenharmony_ci			rc = -ENODEV;
485062306a36Sopenharmony_ci			break;
485162306a36Sopenharmony_ci		}
485262306a36Sopenharmony_ci	}
485362306a36Sopenharmony_ci	info->gpio_present = (rd_reg32(info, JCR) & BIT5) ? 1 : 0;
485462306a36Sopenharmony_ci	info->init_error = rc ? 0 : DiagStatus_AddressFailure;
485562306a36Sopenharmony_ci	return rc;
485662306a36Sopenharmony_ci}
485762306a36Sopenharmony_ci
485862306a36Sopenharmony_cistatic int irq_test(struct slgt_info *info)
485962306a36Sopenharmony_ci{
486062306a36Sopenharmony_ci	unsigned long timeout;
486162306a36Sopenharmony_ci	unsigned long flags;
486262306a36Sopenharmony_ci	struct tty_struct *oldtty = info->port.tty;
486362306a36Sopenharmony_ci	u32 speed = info->params.data_rate;
486462306a36Sopenharmony_ci
486562306a36Sopenharmony_ci	info->params.data_rate = 921600;
486662306a36Sopenharmony_ci	info->port.tty = NULL;
486762306a36Sopenharmony_ci
486862306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock, flags);
486962306a36Sopenharmony_ci	async_mode(info);
487062306a36Sopenharmony_ci	slgt_irq_on(info, IRQ_TXIDLE);
487162306a36Sopenharmony_ci
487262306a36Sopenharmony_ci	/* enable transmitter */
487362306a36Sopenharmony_ci	wr_reg16(info, TCR,
487462306a36Sopenharmony_ci		(unsigned short)(rd_reg16(info, TCR) | BIT1));
487562306a36Sopenharmony_ci
487662306a36Sopenharmony_ci	/* write one byte and wait for tx idle */
487762306a36Sopenharmony_ci	wr_reg16(info, TDR, 0);
487862306a36Sopenharmony_ci
487962306a36Sopenharmony_ci	/* assume failure */
488062306a36Sopenharmony_ci	info->init_error = DiagStatus_IrqFailure;
488162306a36Sopenharmony_ci	info->irq_occurred = false;
488262306a36Sopenharmony_ci
488362306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->lock, flags);
488462306a36Sopenharmony_ci
488562306a36Sopenharmony_ci	timeout=100;
488662306a36Sopenharmony_ci	while(timeout-- && !info->irq_occurred)
488762306a36Sopenharmony_ci		msleep_interruptible(10);
488862306a36Sopenharmony_ci
488962306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
489062306a36Sopenharmony_ci	reset_port(info);
489162306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
489262306a36Sopenharmony_ci
489362306a36Sopenharmony_ci	info->params.data_rate = speed;
489462306a36Sopenharmony_ci	info->port.tty = oldtty;
489562306a36Sopenharmony_ci
489662306a36Sopenharmony_ci	info->init_error = info->irq_occurred ? 0 : DiagStatus_IrqFailure;
489762306a36Sopenharmony_ci	return info->irq_occurred ? 0 : -ENODEV;
489862306a36Sopenharmony_ci}
489962306a36Sopenharmony_ci
490062306a36Sopenharmony_cistatic int loopback_test_rx(struct slgt_info *info)
490162306a36Sopenharmony_ci{
490262306a36Sopenharmony_ci	unsigned char *src, *dest;
490362306a36Sopenharmony_ci	int count;
490462306a36Sopenharmony_ci
490562306a36Sopenharmony_ci	if (desc_complete(info->rbufs[0])) {
490662306a36Sopenharmony_ci		count = desc_count(info->rbufs[0]);
490762306a36Sopenharmony_ci		src   = info->rbufs[0].buf;
490862306a36Sopenharmony_ci		dest  = info->tmp_rbuf;
490962306a36Sopenharmony_ci
491062306a36Sopenharmony_ci		for( ; count ; count-=2, src+=2) {
491162306a36Sopenharmony_ci			/* src=data byte (src+1)=status byte */
491262306a36Sopenharmony_ci			if (!(*(src+1) & (BIT9 + BIT8))) {
491362306a36Sopenharmony_ci				*dest = *src;
491462306a36Sopenharmony_ci				dest++;
491562306a36Sopenharmony_ci				info->tmp_rbuf_count++;
491662306a36Sopenharmony_ci			}
491762306a36Sopenharmony_ci		}
491862306a36Sopenharmony_ci		DBGDATA(info, info->tmp_rbuf, info->tmp_rbuf_count, "rx");
491962306a36Sopenharmony_ci		return 1;
492062306a36Sopenharmony_ci	}
492162306a36Sopenharmony_ci	return 0;
492262306a36Sopenharmony_ci}
492362306a36Sopenharmony_ci
492462306a36Sopenharmony_cistatic int loopback_test(struct slgt_info *info)
492562306a36Sopenharmony_ci{
492662306a36Sopenharmony_ci#define TESTFRAMESIZE 20
492762306a36Sopenharmony_ci
492862306a36Sopenharmony_ci	unsigned long timeout;
492962306a36Sopenharmony_ci	u16 count;
493062306a36Sopenharmony_ci	unsigned char buf[TESTFRAMESIZE];
493162306a36Sopenharmony_ci	int rc = -ENODEV;
493262306a36Sopenharmony_ci	unsigned long flags;
493362306a36Sopenharmony_ci
493462306a36Sopenharmony_ci	struct tty_struct *oldtty = info->port.tty;
493562306a36Sopenharmony_ci	MGSL_PARAMS params;
493662306a36Sopenharmony_ci
493762306a36Sopenharmony_ci	memcpy(&params, &info->params, sizeof(params));
493862306a36Sopenharmony_ci
493962306a36Sopenharmony_ci	info->params.mode = MGSL_MODE_ASYNC;
494062306a36Sopenharmony_ci	info->params.data_rate = 921600;
494162306a36Sopenharmony_ci	info->params.loopback = 1;
494262306a36Sopenharmony_ci	info->port.tty = NULL;
494362306a36Sopenharmony_ci
494462306a36Sopenharmony_ci	/* build and send transmit frame */
494562306a36Sopenharmony_ci	for (count = 0; count < TESTFRAMESIZE; ++count)
494662306a36Sopenharmony_ci		buf[count] = (unsigned char)count;
494762306a36Sopenharmony_ci
494862306a36Sopenharmony_ci	info->tmp_rbuf_count = 0;
494962306a36Sopenharmony_ci	memset(info->tmp_rbuf, 0, TESTFRAMESIZE);
495062306a36Sopenharmony_ci
495162306a36Sopenharmony_ci	/* program hardware for HDLC and enabled receiver */
495262306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
495362306a36Sopenharmony_ci	async_mode(info);
495462306a36Sopenharmony_ci	rx_start(info);
495562306a36Sopenharmony_ci	tx_load(info, buf, count);
495662306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->lock, flags);
495762306a36Sopenharmony_ci
495862306a36Sopenharmony_ci	/* wait for receive complete */
495962306a36Sopenharmony_ci	for (timeout = 100; timeout; --timeout) {
496062306a36Sopenharmony_ci		msleep_interruptible(10);
496162306a36Sopenharmony_ci		if (loopback_test_rx(info)) {
496262306a36Sopenharmony_ci			rc = 0;
496362306a36Sopenharmony_ci			break;
496462306a36Sopenharmony_ci		}
496562306a36Sopenharmony_ci	}
496662306a36Sopenharmony_ci
496762306a36Sopenharmony_ci	/* verify received frame length and contents */
496862306a36Sopenharmony_ci	if (!rc && (info->tmp_rbuf_count != count ||
496962306a36Sopenharmony_ci		  memcmp(buf, info->tmp_rbuf, count))) {
497062306a36Sopenharmony_ci		rc = -ENODEV;
497162306a36Sopenharmony_ci	}
497262306a36Sopenharmony_ci
497362306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
497462306a36Sopenharmony_ci	reset_adapter(info);
497562306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
497662306a36Sopenharmony_ci
497762306a36Sopenharmony_ci	memcpy(&info->params, &params, sizeof(info->params));
497862306a36Sopenharmony_ci	info->port.tty = oldtty;
497962306a36Sopenharmony_ci
498062306a36Sopenharmony_ci	info->init_error = rc ? DiagStatus_DmaFailure : 0;
498162306a36Sopenharmony_ci	return rc;
498262306a36Sopenharmony_ci}
498362306a36Sopenharmony_ci
498462306a36Sopenharmony_cistatic int adapter_test(struct slgt_info *info)
498562306a36Sopenharmony_ci{
498662306a36Sopenharmony_ci	DBGINFO(("testing %s\n", info->device_name));
498762306a36Sopenharmony_ci	if (register_test(info) < 0) {
498862306a36Sopenharmony_ci		printk("register test failure %s addr=%08X\n",
498962306a36Sopenharmony_ci			info->device_name, info->phys_reg_addr);
499062306a36Sopenharmony_ci	} else if (irq_test(info) < 0) {
499162306a36Sopenharmony_ci		printk("IRQ test failure %s IRQ=%d\n",
499262306a36Sopenharmony_ci			info->device_name, info->irq_level);
499362306a36Sopenharmony_ci	} else if (loopback_test(info) < 0) {
499462306a36Sopenharmony_ci		printk("loopback test failure %s\n", info->device_name);
499562306a36Sopenharmony_ci	}
499662306a36Sopenharmony_ci	return info->init_error;
499762306a36Sopenharmony_ci}
499862306a36Sopenharmony_ci
499962306a36Sopenharmony_ci/*
500062306a36Sopenharmony_ci * transmit timeout handler
500162306a36Sopenharmony_ci */
500262306a36Sopenharmony_cistatic void tx_timeout(struct timer_list *t)
500362306a36Sopenharmony_ci{
500462306a36Sopenharmony_ci	struct slgt_info *info = from_timer(info, t, tx_timer);
500562306a36Sopenharmony_ci	unsigned long flags;
500662306a36Sopenharmony_ci
500762306a36Sopenharmony_ci	DBGINFO(("%s tx_timeout\n", info->device_name));
500862306a36Sopenharmony_ci	if(info->tx_active && info->params.mode == MGSL_MODE_HDLC) {
500962306a36Sopenharmony_ci		info->icount.txtimeout++;
501062306a36Sopenharmony_ci	}
501162306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
501262306a36Sopenharmony_ci	tx_stop(info);
501362306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
501462306a36Sopenharmony_ci
501562306a36Sopenharmony_ci#if SYNCLINK_GENERIC_HDLC
501662306a36Sopenharmony_ci	if (info->netcount)
501762306a36Sopenharmony_ci		hdlcdev_tx_done(info);
501862306a36Sopenharmony_ci	else
501962306a36Sopenharmony_ci#endif
502062306a36Sopenharmony_ci		bh_transmit(info);
502162306a36Sopenharmony_ci}
502262306a36Sopenharmony_ci
502362306a36Sopenharmony_ci/*
502462306a36Sopenharmony_ci * receive buffer polling timer
502562306a36Sopenharmony_ci */
502662306a36Sopenharmony_cistatic void rx_timeout(struct timer_list *t)
502762306a36Sopenharmony_ci{
502862306a36Sopenharmony_ci	struct slgt_info *info = from_timer(info, t, rx_timer);
502962306a36Sopenharmony_ci	unsigned long flags;
503062306a36Sopenharmony_ci
503162306a36Sopenharmony_ci	DBGINFO(("%s rx_timeout\n", info->device_name));
503262306a36Sopenharmony_ci	spin_lock_irqsave(&info->lock, flags);
503362306a36Sopenharmony_ci	info->pending_bh |= BH_RECEIVE;
503462306a36Sopenharmony_ci	spin_unlock_irqrestore(&info->lock, flags);
503562306a36Sopenharmony_ci	bh_handler(&info->task);
503662306a36Sopenharmony_ci}
503762306a36Sopenharmony_ci
5038