18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-1.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Device driver for Microgate SyncLink GT serial adapters.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * written by Paul Fulghum for Microgate Corporation
68c2ecf20Sopenharmony_ci * paulkf@microgate.com
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Microgate and SyncLink are trademarks of Microgate Corporation
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
118c2ecf20Sopenharmony_ci * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
128c2ecf20Sopenharmony_ci * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
138c2ecf20Sopenharmony_ci * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
148c2ecf20Sopenharmony_ci * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
158c2ecf20Sopenharmony_ci * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
168c2ecf20Sopenharmony_ci * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
178c2ecf20Sopenharmony_ci * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
188c2ecf20Sopenharmony_ci * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
198c2ecf20Sopenharmony_ci * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
208c2ecf20Sopenharmony_ci * OF THE POSSIBILITY OF SUCH DAMAGE.
218c2ecf20Sopenharmony_ci */
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/*
248c2ecf20Sopenharmony_ci * DEBUG OUTPUT DEFINITIONS
258c2ecf20Sopenharmony_ci *
268c2ecf20Sopenharmony_ci * uncomment lines below to enable specific types of debug output
278c2ecf20Sopenharmony_ci *
288c2ecf20Sopenharmony_ci * DBGINFO   information - most verbose output
298c2ecf20Sopenharmony_ci * DBGERR    serious errors
308c2ecf20Sopenharmony_ci * DBGBH     bottom half service routine debugging
318c2ecf20Sopenharmony_ci * DBGISR    interrupt service routine debugging
328c2ecf20Sopenharmony_ci * DBGDATA   output receive and transmit data
338c2ecf20Sopenharmony_ci * DBGTBUF   output transmit DMA buffers and registers
348c2ecf20Sopenharmony_ci * DBGRBUF   output receive DMA buffers and registers
358c2ecf20Sopenharmony_ci */
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#define DBGINFO(fmt) if (debug_level >= DEBUG_LEVEL_INFO) printk fmt
388c2ecf20Sopenharmony_ci#define DBGERR(fmt) if (debug_level >= DEBUG_LEVEL_ERROR) printk fmt
398c2ecf20Sopenharmony_ci#define DBGBH(fmt) if (debug_level >= DEBUG_LEVEL_BH) printk fmt
408c2ecf20Sopenharmony_ci#define DBGISR(fmt) if (debug_level >= DEBUG_LEVEL_ISR) printk fmt
418c2ecf20Sopenharmony_ci#define DBGDATA(info, buf, size, label) if (debug_level >= DEBUG_LEVEL_DATA) trace_block((info), (buf), (size), (label))
428c2ecf20Sopenharmony_ci/*#define DBGTBUF(info) dump_tbufs(info)*/
438c2ecf20Sopenharmony_ci/*#define DBGRBUF(info) dump_rbufs(info)*/
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#include <linux/module.h>
478c2ecf20Sopenharmony_ci#include <linux/errno.h>
488c2ecf20Sopenharmony_ci#include <linux/signal.h>
498c2ecf20Sopenharmony_ci#include <linux/sched.h>
508c2ecf20Sopenharmony_ci#include <linux/timer.h>
518c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
528c2ecf20Sopenharmony_ci#include <linux/pci.h>
538c2ecf20Sopenharmony_ci#include <linux/tty.h>
548c2ecf20Sopenharmony_ci#include <linux/tty_flip.h>
558c2ecf20Sopenharmony_ci#include <linux/serial.h>
568c2ecf20Sopenharmony_ci#include <linux/major.h>
578c2ecf20Sopenharmony_ci#include <linux/string.h>
588c2ecf20Sopenharmony_ci#include <linux/fcntl.h>
598c2ecf20Sopenharmony_ci#include <linux/ptrace.h>
608c2ecf20Sopenharmony_ci#include <linux/ioport.h>
618c2ecf20Sopenharmony_ci#include <linux/mm.h>
628c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
638c2ecf20Sopenharmony_ci#include <linux/slab.h>
648c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
658c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
668c2ecf20Sopenharmony_ci#include <linux/init.h>
678c2ecf20Sopenharmony_ci#include <linux/delay.h>
688c2ecf20Sopenharmony_ci#include <linux/ioctl.h>
698c2ecf20Sopenharmony_ci#include <linux/termios.h>
708c2ecf20Sopenharmony_ci#include <linux/bitops.h>
718c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
728c2ecf20Sopenharmony_ci#include <linux/hdlc.h>
738c2ecf20Sopenharmony_ci#include <linux/synclink.h>
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci#include <asm/io.h>
768c2ecf20Sopenharmony_ci#include <asm/irq.h>
778c2ecf20Sopenharmony_ci#include <asm/dma.h>
788c2ecf20Sopenharmony_ci#include <asm/types.h>
798c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci#if defined(CONFIG_HDLC) || (defined(CONFIG_HDLC_MODULE) && defined(CONFIG_SYNCLINK_GT_MODULE))
828c2ecf20Sopenharmony_ci#define SYNCLINK_GENERIC_HDLC 1
838c2ecf20Sopenharmony_ci#else
848c2ecf20Sopenharmony_ci#define SYNCLINK_GENERIC_HDLC 0
858c2ecf20Sopenharmony_ci#endif
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci/*
888c2ecf20Sopenharmony_ci * module identification
898c2ecf20Sopenharmony_ci */
908c2ecf20Sopenharmony_cistatic char *driver_name     = "SyncLink GT";
918c2ecf20Sopenharmony_cistatic char *slgt_driver_name = "synclink_gt";
928c2ecf20Sopenharmony_cistatic char *tty_dev_prefix  = "ttySLG";
938c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
948c2ecf20Sopenharmony_ci#define MGSL_MAGIC 0x5401
958c2ecf20Sopenharmony_ci#define MAX_DEVICES 32
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic const struct pci_device_id pci_table[] = {
988c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_MICROGATE, SYNCLINK_GT_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
998c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_MICROGATE, SYNCLINK_GT2_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
1008c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_MICROGATE, SYNCLINK_GT4_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
1018c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_MICROGATE, SYNCLINK_AC_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
1028c2ecf20Sopenharmony_ci	{0,}, /* terminate list */
1038c2ecf20Sopenharmony_ci};
1048c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pci_table);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic int  init_one(struct pci_dev *dev,const struct pci_device_id *ent);
1078c2ecf20Sopenharmony_cistatic void remove_one(struct pci_dev *dev);
1088c2ecf20Sopenharmony_cistatic struct pci_driver pci_driver = {
1098c2ecf20Sopenharmony_ci	.name		= "synclink_gt",
1108c2ecf20Sopenharmony_ci	.id_table	= pci_table,
1118c2ecf20Sopenharmony_ci	.probe		= init_one,
1128c2ecf20Sopenharmony_ci	.remove		= remove_one,
1138c2ecf20Sopenharmony_ci};
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic bool pci_registered;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci/*
1188c2ecf20Sopenharmony_ci * module configuration and status
1198c2ecf20Sopenharmony_ci */
1208c2ecf20Sopenharmony_cistatic struct slgt_info *slgt_device_list;
1218c2ecf20Sopenharmony_cistatic int slgt_device_count;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic int ttymajor;
1248c2ecf20Sopenharmony_cistatic int debug_level;
1258c2ecf20Sopenharmony_cistatic int maxframe[MAX_DEVICES];
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cimodule_param(ttymajor, int, 0);
1288c2ecf20Sopenharmony_cimodule_param(debug_level, int, 0);
1298c2ecf20Sopenharmony_cimodule_param_array(maxframe, int, NULL, 0);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ttymajor, "TTY major device number override: 0=auto assigned");
1328c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug_level, "Debug syslog output: 0=disabled, 1 to 5=increasing detail");
1338c2ecf20Sopenharmony_ciMODULE_PARM_DESC(maxframe, "Maximum frame size used by device (4096 to 65535)");
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci/*
1368c2ecf20Sopenharmony_ci * tty support and callbacks
1378c2ecf20Sopenharmony_ci */
1388c2ecf20Sopenharmony_cistatic struct tty_driver *serial_driver;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic void wait_until_sent(struct tty_struct *tty, int timeout);
1418c2ecf20Sopenharmony_cistatic void flush_buffer(struct tty_struct *tty);
1428c2ecf20Sopenharmony_cistatic void tx_release(struct tty_struct *tty);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci/*
1458c2ecf20Sopenharmony_ci * generic HDLC support
1468c2ecf20Sopenharmony_ci */
1478c2ecf20Sopenharmony_ci#define dev_to_port(D) (dev_to_hdlc(D)->priv)
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci/*
1518c2ecf20Sopenharmony_ci * device specific structures, macros and functions
1528c2ecf20Sopenharmony_ci */
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci#define SLGT_MAX_PORTS 4
1558c2ecf20Sopenharmony_ci#define SLGT_REG_SIZE  256
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci/*
1588c2ecf20Sopenharmony_ci * conditional wait facility
1598c2ecf20Sopenharmony_ci */
1608c2ecf20Sopenharmony_cistruct cond_wait {
1618c2ecf20Sopenharmony_ci	struct cond_wait *next;
1628c2ecf20Sopenharmony_ci	wait_queue_head_t q;
1638c2ecf20Sopenharmony_ci	wait_queue_entry_t wait;
1648c2ecf20Sopenharmony_ci	unsigned int data;
1658c2ecf20Sopenharmony_ci};
1668c2ecf20Sopenharmony_cistatic void flush_cond_wait(struct cond_wait **head);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci/*
1698c2ecf20Sopenharmony_ci * DMA buffer descriptor and access macros
1708c2ecf20Sopenharmony_ci */
1718c2ecf20Sopenharmony_cistruct slgt_desc
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	__le16 count;
1748c2ecf20Sopenharmony_ci	__le16 status;
1758c2ecf20Sopenharmony_ci	__le32 pbuf;  /* physical address of data buffer */
1768c2ecf20Sopenharmony_ci	__le32 next;  /* physical address of next descriptor */
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	/* driver book keeping */
1798c2ecf20Sopenharmony_ci	char *buf;          /* virtual  address of data buffer */
1808c2ecf20Sopenharmony_ci    	unsigned int pdesc; /* physical address of this descriptor */
1818c2ecf20Sopenharmony_ci	dma_addr_t buf_dma_addr;
1828c2ecf20Sopenharmony_ci	unsigned short buf_count;
1838c2ecf20Sopenharmony_ci};
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci#define set_desc_buffer(a,b) (a).pbuf = cpu_to_le32((unsigned int)(b))
1868c2ecf20Sopenharmony_ci#define set_desc_next(a,b) (a).next   = cpu_to_le32((unsigned int)(b))
1878c2ecf20Sopenharmony_ci#define set_desc_count(a,b)(a).count  = cpu_to_le16((unsigned short)(b))
1888c2ecf20Sopenharmony_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))
1898c2ecf20Sopenharmony_ci#define set_desc_status(a, b) (a).status = cpu_to_le16((unsigned short)(b))
1908c2ecf20Sopenharmony_ci#define desc_count(a)      (le16_to_cpu((a).count))
1918c2ecf20Sopenharmony_ci#define desc_status(a)     (le16_to_cpu((a).status))
1928c2ecf20Sopenharmony_ci#define desc_complete(a)   (le16_to_cpu((a).status) & BIT15)
1938c2ecf20Sopenharmony_ci#define desc_eof(a)        (le16_to_cpu((a).status) & BIT2)
1948c2ecf20Sopenharmony_ci#define desc_crc_error(a)  (le16_to_cpu((a).status) & BIT1)
1958c2ecf20Sopenharmony_ci#define desc_abort(a)      (le16_to_cpu((a).status) & BIT0)
1968c2ecf20Sopenharmony_ci#define desc_residue(a)    ((le16_to_cpu((a).status) & 0x38) >> 3)
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistruct _input_signal_events {
1998c2ecf20Sopenharmony_ci	int ri_up;
2008c2ecf20Sopenharmony_ci	int ri_down;
2018c2ecf20Sopenharmony_ci	int dsr_up;
2028c2ecf20Sopenharmony_ci	int dsr_down;
2038c2ecf20Sopenharmony_ci	int dcd_up;
2048c2ecf20Sopenharmony_ci	int dcd_down;
2058c2ecf20Sopenharmony_ci	int cts_up;
2068c2ecf20Sopenharmony_ci	int cts_down;
2078c2ecf20Sopenharmony_ci};
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci/*
2108c2ecf20Sopenharmony_ci * device instance data structure
2118c2ecf20Sopenharmony_ci */
2128c2ecf20Sopenharmony_cistruct slgt_info {
2138c2ecf20Sopenharmony_ci	void *if_ptr;		/* General purpose pointer (used by SPPP) */
2148c2ecf20Sopenharmony_ci	struct tty_port port;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	struct slgt_info *next_device;	/* device list link */
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	int magic;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	char device_name[25];
2218c2ecf20Sopenharmony_ci	struct pci_dev *pdev;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	int port_count;  /* count of ports on adapter */
2248c2ecf20Sopenharmony_ci	int adapter_num; /* adapter instance number */
2258c2ecf20Sopenharmony_ci	int port_num;    /* port instance number */
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	/* array of pointers to port contexts on this adapter */
2288c2ecf20Sopenharmony_ci	struct slgt_info *port_array[SLGT_MAX_PORTS];
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	int			line;		/* tty line instance number */
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	struct mgsl_icount	icount;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	int			timeout;
2358c2ecf20Sopenharmony_ci	int			x_char;		/* xon/xoff character */
2368c2ecf20Sopenharmony_ci	unsigned int		read_status_mask;
2378c2ecf20Sopenharmony_ci	unsigned int 		ignore_status_mask;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	wait_queue_head_t	status_event_wait_q;
2408c2ecf20Sopenharmony_ci	wait_queue_head_t	event_wait_q;
2418c2ecf20Sopenharmony_ci	struct timer_list	tx_timer;
2428c2ecf20Sopenharmony_ci	struct timer_list	rx_timer;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	unsigned int            gpio_present;
2458c2ecf20Sopenharmony_ci	struct cond_wait        *gpio_wait_q;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	spinlock_t lock;	/* spinlock for synchronizing with ISR */
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	struct work_struct task;
2508c2ecf20Sopenharmony_ci	u32 pending_bh;
2518c2ecf20Sopenharmony_ci	bool bh_requested;
2528c2ecf20Sopenharmony_ci	bool bh_running;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	int isr_overflow;
2558c2ecf20Sopenharmony_ci	bool irq_requested;	/* true if IRQ requested */
2568c2ecf20Sopenharmony_ci	bool irq_occurred;	/* for diagnostics use */
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	/* device configuration */
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	unsigned int bus_type;
2618c2ecf20Sopenharmony_ci	unsigned int irq_level;
2628c2ecf20Sopenharmony_ci	unsigned long irq_flags;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	unsigned char __iomem * reg_addr;  /* memory mapped registers address */
2658c2ecf20Sopenharmony_ci	u32 phys_reg_addr;
2668c2ecf20Sopenharmony_ci	bool reg_addr_requested;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	MGSL_PARAMS params;       /* communications parameters */
2698c2ecf20Sopenharmony_ci	u32 idle_mode;
2708c2ecf20Sopenharmony_ci	u32 max_frame_size;       /* as set by device config */
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	unsigned int rbuf_fill_level;
2738c2ecf20Sopenharmony_ci	unsigned int rx_pio;
2748c2ecf20Sopenharmony_ci	unsigned int if_mode;
2758c2ecf20Sopenharmony_ci	unsigned int base_clock;
2768c2ecf20Sopenharmony_ci	unsigned int xsync;
2778c2ecf20Sopenharmony_ci	unsigned int xctrl;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	/* device status */
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	bool rx_enabled;
2828c2ecf20Sopenharmony_ci	bool rx_restart;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	bool tx_enabled;
2858c2ecf20Sopenharmony_ci	bool tx_active;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	unsigned char signals;    /* serial signal states */
2888c2ecf20Sopenharmony_ci	int init_error;  /* initialization error */
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	unsigned char *tx_buf;
2918c2ecf20Sopenharmony_ci	int tx_count;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	char *flag_buf;
2948c2ecf20Sopenharmony_ci	bool drop_rts_on_tx_done;
2958c2ecf20Sopenharmony_ci	struct	_input_signal_events	input_signal_events;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	int dcd_chkcount;	/* check counts to prevent */
2988c2ecf20Sopenharmony_ci	int cts_chkcount;	/* too many IRQs if a signal */
2998c2ecf20Sopenharmony_ci	int dsr_chkcount;	/* is floating */
3008c2ecf20Sopenharmony_ci	int ri_chkcount;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	char *bufs;		/* virtual address of DMA buffer lists */
3038c2ecf20Sopenharmony_ci	dma_addr_t bufs_dma_addr; /* physical address of buffer descriptors */
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	unsigned int rbuf_count;
3068c2ecf20Sopenharmony_ci	struct slgt_desc *rbufs;
3078c2ecf20Sopenharmony_ci	unsigned int rbuf_current;
3088c2ecf20Sopenharmony_ci	unsigned int rbuf_index;
3098c2ecf20Sopenharmony_ci	unsigned int rbuf_fill_index;
3108c2ecf20Sopenharmony_ci	unsigned short rbuf_fill_count;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	unsigned int tbuf_count;
3138c2ecf20Sopenharmony_ci	struct slgt_desc *tbufs;
3148c2ecf20Sopenharmony_ci	unsigned int tbuf_current;
3158c2ecf20Sopenharmony_ci	unsigned int tbuf_start;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	unsigned char *tmp_rbuf;
3188c2ecf20Sopenharmony_ci	unsigned int tmp_rbuf_count;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	/* SPPP/Cisco HDLC device parts */
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	int netcount;
3238c2ecf20Sopenharmony_ci	spinlock_t netlock;
3248c2ecf20Sopenharmony_ci#if SYNCLINK_GENERIC_HDLC
3258c2ecf20Sopenharmony_ci	struct net_device *netdev;
3268c2ecf20Sopenharmony_ci#endif
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci};
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cistatic MGSL_PARAMS default_params = {
3318c2ecf20Sopenharmony_ci	.mode            = MGSL_MODE_HDLC,
3328c2ecf20Sopenharmony_ci	.loopback        = 0,
3338c2ecf20Sopenharmony_ci	.flags           = HDLC_FLAG_UNDERRUN_ABORT15,
3348c2ecf20Sopenharmony_ci	.encoding        = HDLC_ENCODING_NRZI_SPACE,
3358c2ecf20Sopenharmony_ci	.clock_speed     = 0,
3368c2ecf20Sopenharmony_ci	.addr_filter     = 0xff,
3378c2ecf20Sopenharmony_ci	.crc_type        = HDLC_CRC_16_CCITT,
3388c2ecf20Sopenharmony_ci	.preamble_length = HDLC_PREAMBLE_LENGTH_8BITS,
3398c2ecf20Sopenharmony_ci	.preamble        = HDLC_PREAMBLE_PATTERN_NONE,
3408c2ecf20Sopenharmony_ci	.data_rate       = 9600,
3418c2ecf20Sopenharmony_ci	.data_bits       = 8,
3428c2ecf20Sopenharmony_ci	.stop_bits       = 1,
3438c2ecf20Sopenharmony_ci	.parity          = ASYNC_PARITY_NONE
3448c2ecf20Sopenharmony_ci};
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci#define BH_RECEIVE  1
3488c2ecf20Sopenharmony_ci#define BH_TRANSMIT 2
3498c2ecf20Sopenharmony_ci#define BH_STATUS   4
3508c2ecf20Sopenharmony_ci#define IO_PIN_SHUTDOWN_LIMIT 100
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci#define DMABUFSIZE 256
3538c2ecf20Sopenharmony_ci#define DESC_LIST_SIZE 4096
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci#define MASK_PARITY  BIT1
3568c2ecf20Sopenharmony_ci#define MASK_FRAMING BIT0
3578c2ecf20Sopenharmony_ci#define MASK_BREAK   BIT14
3588c2ecf20Sopenharmony_ci#define MASK_OVERRUN BIT4
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci#define GSR   0x00 /* global status */
3618c2ecf20Sopenharmony_ci#define JCR   0x04 /* JTAG control */
3628c2ecf20Sopenharmony_ci#define IODR  0x08 /* GPIO direction */
3638c2ecf20Sopenharmony_ci#define IOER  0x0c /* GPIO interrupt enable */
3648c2ecf20Sopenharmony_ci#define IOVR  0x10 /* GPIO value */
3658c2ecf20Sopenharmony_ci#define IOSR  0x14 /* GPIO interrupt status */
3668c2ecf20Sopenharmony_ci#define TDR   0x80 /* tx data */
3678c2ecf20Sopenharmony_ci#define RDR   0x80 /* rx data */
3688c2ecf20Sopenharmony_ci#define TCR   0x82 /* tx control */
3698c2ecf20Sopenharmony_ci#define TIR   0x84 /* tx idle */
3708c2ecf20Sopenharmony_ci#define TPR   0x85 /* tx preamble */
3718c2ecf20Sopenharmony_ci#define RCR   0x86 /* rx control */
3728c2ecf20Sopenharmony_ci#define VCR   0x88 /* V.24 control */
3738c2ecf20Sopenharmony_ci#define CCR   0x89 /* clock control */
3748c2ecf20Sopenharmony_ci#define BDR   0x8a /* baud divisor */
3758c2ecf20Sopenharmony_ci#define SCR   0x8c /* serial control */
3768c2ecf20Sopenharmony_ci#define SSR   0x8e /* serial status */
3778c2ecf20Sopenharmony_ci#define RDCSR 0x90 /* rx DMA control/status */
3788c2ecf20Sopenharmony_ci#define TDCSR 0x94 /* tx DMA control/status */
3798c2ecf20Sopenharmony_ci#define RDDAR 0x98 /* rx DMA descriptor address */
3808c2ecf20Sopenharmony_ci#define TDDAR 0x9c /* tx DMA descriptor address */
3818c2ecf20Sopenharmony_ci#define XSR   0x40 /* extended sync pattern */
3828c2ecf20Sopenharmony_ci#define XCR   0x44 /* extended control */
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci#define RXIDLE      BIT14
3858c2ecf20Sopenharmony_ci#define RXBREAK     BIT14
3868c2ecf20Sopenharmony_ci#define IRQ_TXDATA  BIT13
3878c2ecf20Sopenharmony_ci#define IRQ_TXIDLE  BIT12
3888c2ecf20Sopenharmony_ci#define IRQ_TXUNDER BIT11 /* HDLC */
3898c2ecf20Sopenharmony_ci#define IRQ_RXDATA  BIT10
3908c2ecf20Sopenharmony_ci#define IRQ_RXIDLE  BIT9  /* HDLC */
3918c2ecf20Sopenharmony_ci#define IRQ_RXBREAK BIT9  /* async */
3928c2ecf20Sopenharmony_ci#define IRQ_RXOVER  BIT8
3938c2ecf20Sopenharmony_ci#define IRQ_DSR     BIT7
3948c2ecf20Sopenharmony_ci#define IRQ_CTS     BIT6
3958c2ecf20Sopenharmony_ci#define IRQ_DCD     BIT5
3968c2ecf20Sopenharmony_ci#define IRQ_RI      BIT4
3978c2ecf20Sopenharmony_ci#define IRQ_ALL     0x3ff0
3988c2ecf20Sopenharmony_ci#define IRQ_MASTER  BIT0
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci#define slgt_irq_on(info, mask) \
4018c2ecf20Sopenharmony_ci	wr_reg16((info), SCR, (unsigned short)(rd_reg16((info), SCR) | (mask)))
4028c2ecf20Sopenharmony_ci#define slgt_irq_off(info, mask) \
4038c2ecf20Sopenharmony_ci	wr_reg16((info), SCR, (unsigned short)(rd_reg16((info), SCR) & ~(mask)))
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_cistatic __u8  rd_reg8(struct slgt_info *info, unsigned int addr);
4068c2ecf20Sopenharmony_cistatic void  wr_reg8(struct slgt_info *info, unsigned int addr, __u8 value);
4078c2ecf20Sopenharmony_cistatic __u16 rd_reg16(struct slgt_info *info, unsigned int addr);
4088c2ecf20Sopenharmony_cistatic void  wr_reg16(struct slgt_info *info, unsigned int addr, __u16 value);
4098c2ecf20Sopenharmony_cistatic __u32 rd_reg32(struct slgt_info *info, unsigned int addr);
4108c2ecf20Sopenharmony_cistatic void  wr_reg32(struct slgt_info *info, unsigned int addr, __u32 value);
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_cistatic void  msc_set_vcr(struct slgt_info *info);
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_cistatic int  startup(struct slgt_info *info);
4158c2ecf20Sopenharmony_cistatic int  block_til_ready(struct tty_struct *tty, struct file * filp,struct slgt_info *info);
4168c2ecf20Sopenharmony_cistatic void shutdown(struct slgt_info *info);
4178c2ecf20Sopenharmony_cistatic void program_hw(struct slgt_info *info);
4188c2ecf20Sopenharmony_cistatic void change_params(struct slgt_info *info);
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_cistatic int  adapter_test(struct slgt_info *info);
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_cistatic void reset_port(struct slgt_info *info);
4238c2ecf20Sopenharmony_cistatic void async_mode(struct slgt_info *info);
4248c2ecf20Sopenharmony_cistatic void sync_mode(struct slgt_info *info);
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_cistatic void rx_stop(struct slgt_info *info);
4278c2ecf20Sopenharmony_cistatic void rx_start(struct slgt_info *info);
4288c2ecf20Sopenharmony_cistatic void reset_rbufs(struct slgt_info *info);
4298c2ecf20Sopenharmony_cistatic void free_rbufs(struct slgt_info *info, unsigned int first, unsigned int last);
4308c2ecf20Sopenharmony_cistatic bool rx_get_frame(struct slgt_info *info);
4318c2ecf20Sopenharmony_cistatic bool rx_get_buf(struct slgt_info *info);
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_cistatic void tx_start(struct slgt_info *info);
4348c2ecf20Sopenharmony_cistatic void tx_stop(struct slgt_info *info);
4358c2ecf20Sopenharmony_cistatic void tx_set_idle(struct slgt_info *info);
4368c2ecf20Sopenharmony_cistatic unsigned int tbuf_bytes(struct slgt_info *info);
4378c2ecf20Sopenharmony_cistatic void reset_tbufs(struct slgt_info *info);
4388c2ecf20Sopenharmony_cistatic void tdma_reset(struct slgt_info *info);
4398c2ecf20Sopenharmony_cistatic bool tx_load(struct slgt_info *info, const char *buf, unsigned int count);
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_cistatic void get_gtsignals(struct slgt_info *info);
4428c2ecf20Sopenharmony_cistatic void set_gtsignals(struct slgt_info *info);
4438c2ecf20Sopenharmony_cistatic void set_rate(struct slgt_info *info, u32 data_rate);
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_cistatic void bh_transmit(struct slgt_info *info);
4468c2ecf20Sopenharmony_cistatic void isr_txeom(struct slgt_info *info, unsigned short status);
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_cistatic void tx_timeout(struct timer_list *t);
4498c2ecf20Sopenharmony_cistatic void rx_timeout(struct timer_list *t);
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci/*
4528c2ecf20Sopenharmony_ci * ioctl handlers
4538c2ecf20Sopenharmony_ci */
4548c2ecf20Sopenharmony_cistatic int  get_stats(struct slgt_info *info, struct mgsl_icount __user *user_icount);
4558c2ecf20Sopenharmony_cistatic int  get_params(struct slgt_info *info, MGSL_PARAMS __user *params);
4568c2ecf20Sopenharmony_cistatic int  set_params(struct slgt_info *info, MGSL_PARAMS __user *params);
4578c2ecf20Sopenharmony_cistatic int  get_txidle(struct slgt_info *info, int __user *idle_mode);
4588c2ecf20Sopenharmony_cistatic int  set_txidle(struct slgt_info *info, int idle_mode);
4598c2ecf20Sopenharmony_cistatic int  tx_enable(struct slgt_info *info, int enable);
4608c2ecf20Sopenharmony_cistatic int  tx_abort(struct slgt_info *info);
4618c2ecf20Sopenharmony_cistatic int  rx_enable(struct slgt_info *info, int enable);
4628c2ecf20Sopenharmony_cistatic int  modem_input_wait(struct slgt_info *info,int arg);
4638c2ecf20Sopenharmony_cistatic int  wait_mgsl_event(struct slgt_info *info, int __user *mask_ptr);
4648c2ecf20Sopenharmony_cistatic int  get_interface(struct slgt_info *info, int __user *if_mode);
4658c2ecf20Sopenharmony_cistatic int  set_interface(struct slgt_info *info, int if_mode);
4668c2ecf20Sopenharmony_cistatic int  set_gpio(struct slgt_info *info, struct gpio_desc __user *gpio);
4678c2ecf20Sopenharmony_cistatic int  get_gpio(struct slgt_info *info, struct gpio_desc __user *gpio);
4688c2ecf20Sopenharmony_cistatic int  wait_gpio(struct slgt_info *info, struct gpio_desc __user *gpio);
4698c2ecf20Sopenharmony_cistatic int  get_xsync(struct slgt_info *info, int __user *if_mode);
4708c2ecf20Sopenharmony_cistatic int  set_xsync(struct slgt_info *info, int if_mode);
4718c2ecf20Sopenharmony_cistatic int  get_xctrl(struct slgt_info *info, int __user *if_mode);
4728c2ecf20Sopenharmony_cistatic int  set_xctrl(struct slgt_info *info, int if_mode);
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci/*
4758c2ecf20Sopenharmony_ci * driver functions
4768c2ecf20Sopenharmony_ci */
4778c2ecf20Sopenharmony_cistatic void release_resources(struct slgt_info *info);
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci/*
4808c2ecf20Sopenharmony_ci * DEBUG OUTPUT CODE
4818c2ecf20Sopenharmony_ci */
4828c2ecf20Sopenharmony_ci#ifndef DBGINFO
4838c2ecf20Sopenharmony_ci#define DBGINFO(fmt)
4848c2ecf20Sopenharmony_ci#endif
4858c2ecf20Sopenharmony_ci#ifndef DBGERR
4868c2ecf20Sopenharmony_ci#define DBGERR(fmt)
4878c2ecf20Sopenharmony_ci#endif
4888c2ecf20Sopenharmony_ci#ifndef DBGBH
4898c2ecf20Sopenharmony_ci#define DBGBH(fmt)
4908c2ecf20Sopenharmony_ci#endif
4918c2ecf20Sopenharmony_ci#ifndef DBGISR
4928c2ecf20Sopenharmony_ci#define DBGISR(fmt)
4938c2ecf20Sopenharmony_ci#endif
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci#ifdef DBGDATA
4968c2ecf20Sopenharmony_cistatic void trace_block(struct slgt_info *info, const char *data, int count, const char *label)
4978c2ecf20Sopenharmony_ci{
4988c2ecf20Sopenharmony_ci	int i;
4998c2ecf20Sopenharmony_ci	int linecount;
5008c2ecf20Sopenharmony_ci	printk("%s %s data:\n",info->device_name, label);
5018c2ecf20Sopenharmony_ci	while(count) {
5028c2ecf20Sopenharmony_ci		linecount = (count > 16) ? 16 : count;
5038c2ecf20Sopenharmony_ci		for(i=0; i < linecount; i++)
5048c2ecf20Sopenharmony_ci			printk("%02X ",(unsigned char)data[i]);
5058c2ecf20Sopenharmony_ci		for(;i<17;i++)
5068c2ecf20Sopenharmony_ci			printk("   ");
5078c2ecf20Sopenharmony_ci		for(i=0;i<linecount;i++) {
5088c2ecf20Sopenharmony_ci			if (data[i]>=040 && data[i]<=0176)
5098c2ecf20Sopenharmony_ci				printk("%c",data[i]);
5108c2ecf20Sopenharmony_ci			else
5118c2ecf20Sopenharmony_ci				printk(".");
5128c2ecf20Sopenharmony_ci		}
5138c2ecf20Sopenharmony_ci		printk("\n");
5148c2ecf20Sopenharmony_ci		data  += linecount;
5158c2ecf20Sopenharmony_ci		count -= linecount;
5168c2ecf20Sopenharmony_ci	}
5178c2ecf20Sopenharmony_ci}
5188c2ecf20Sopenharmony_ci#else
5198c2ecf20Sopenharmony_ci#define DBGDATA(info, buf, size, label)
5208c2ecf20Sopenharmony_ci#endif
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci#ifdef DBGTBUF
5238c2ecf20Sopenharmony_cistatic void dump_tbufs(struct slgt_info *info)
5248c2ecf20Sopenharmony_ci{
5258c2ecf20Sopenharmony_ci	int i;
5268c2ecf20Sopenharmony_ci	printk("tbuf_current=%d\n", info->tbuf_current);
5278c2ecf20Sopenharmony_ci	for (i=0 ; i < info->tbuf_count ; i++) {
5288c2ecf20Sopenharmony_ci		printk("%d: count=%04X status=%04X\n",
5298c2ecf20Sopenharmony_ci			i, le16_to_cpu(info->tbufs[i].count), le16_to_cpu(info->tbufs[i].status));
5308c2ecf20Sopenharmony_ci	}
5318c2ecf20Sopenharmony_ci}
5328c2ecf20Sopenharmony_ci#else
5338c2ecf20Sopenharmony_ci#define DBGTBUF(info)
5348c2ecf20Sopenharmony_ci#endif
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci#ifdef DBGRBUF
5378c2ecf20Sopenharmony_cistatic void dump_rbufs(struct slgt_info *info)
5388c2ecf20Sopenharmony_ci{
5398c2ecf20Sopenharmony_ci	int i;
5408c2ecf20Sopenharmony_ci	printk("rbuf_current=%d\n", info->rbuf_current);
5418c2ecf20Sopenharmony_ci	for (i=0 ; i < info->rbuf_count ; i++) {
5428c2ecf20Sopenharmony_ci		printk("%d: count=%04X status=%04X\n",
5438c2ecf20Sopenharmony_ci			i, le16_to_cpu(info->rbufs[i].count), le16_to_cpu(info->rbufs[i].status));
5448c2ecf20Sopenharmony_ci	}
5458c2ecf20Sopenharmony_ci}
5468c2ecf20Sopenharmony_ci#else
5478c2ecf20Sopenharmony_ci#define DBGRBUF(info)
5488c2ecf20Sopenharmony_ci#endif
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_cistatic inline int sanity_check(struct slgt_info *info, char *devname, const char *name)
5518c2ecf20Sopenharmony_ci{
5528c2ecf20Sopenharmony_ci#ifdef SANITY_CHECK
5538c2ecf20Sopenharmony_ci	if (!info) {
5548c2ecf20Sopenharmony_ci		printk("null struct slgt_info for (%s) in %s\n", devname, name);
5558c2ecf20Sopenharmony_ci		return 1;
5568c2ecf20Sopenharmony_ci	}
5578c2ecf20Sopenharmony_ci	if (info->magic != MGSL_MAGIC) {
5588c2ecf20Sopenharmony_ci		printk("bad magic number struct slgt_info (%s) in %s\n", devname, name);
5598c2ecf20Sopenharmony_ci		return 1;
5608c2ecf20Sopenharmony_ci	}
5618c2ecf20Sopenharmony_ci#else
5628c2ecf20Sopenharmony_ci	if (!info)
5638c2ecf20Sopenharmony_ci		return 1;
5648c2ecf20Sopenharmony_ci#endif
5658c2ecf20Sopenharmony_ci	return 0;
5668c2ecf20Sopenharmony_ci}
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci/**
5698c2ecf20Sopenharmony_ci * line discipline callback wrappers
5708c2ecf20Sopenharmony_ci *
5718c2ecf20Sopenharmony_ci * The wrappers maintain line discipline references
5728c2ecf20Sopenharmony_ci * while calling into the line discipline.
5738c2ecf20Sopenharmony_ci *
5748c2ecf20Sopenharmony_ci * ldisc_receive_buf  - pass receive data to line discipline
5758c2ecf20Sopenharmony_ci */
5768c2ecf20Sopenharmony_cistatic void ldisc_receive_buf(struct tty_struct *tty,
5778c2ecf20Sopenharmony_ci			      const __u8 *data, char *flags, int count)
5788c2ecf20Sopenharmony_ci{
5798c2ecf20Sopenharmony_ci	struct tty_ldisc *ld;
5808c2ecf20Sopenharmony_ci	if (!tty)
5818c2ecf20Sopenharmony_ci		return;
5828c2ecf20Sopenharmony_ci	ld = tty_ldisc_ref(tty);
5838c2ecf20Sopenharmony_ci	if (ld) {
5848c2ecf20Sopenharmony_ci		if (ld->ops->receive_buf)
5858c2ecf20Sopenharmony_ci			ld->ops->receive_buf(tty, data, flags, count);
5868c2ecf20Sopenharmony_ci		tty_ldisc_deref(ld);
5878c2ecf20Sopenharmony_ci	}
5888c2ecf20Sopenharmony_ci}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci/* tty callbacks */
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_cistatic int open(struct tty_struct *tty, struct file *filp)
5938c2ecf20Sopenharmony_ci{
5948c2ecf20Sopenharmony_ci	struct slgt_info *info;
5958c2ecf20Sopenharmony_ci	int retval, line;
5968c2ecf20Sopenharmony_ci	unsigned long flags;
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	line = tty->index;
5998c2ecf20Sopenharmony_ci	if (line >= slgt_device_count) {
6008c2ecf20Sopenharmony_ci		DBGERR(("%s: open with invalid line #%d.\n", driver_name, line));
6018c2ecf20Sopenharmony_ci		return -ENODEV;
6028c2ecf20Sopenharmony_ci	}
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	info = slgt_device_list;
6058c2ecf20Sopenharmony_ci	while(info && info->line != line)
6068c2ecf20Sopenharmony_ci		info = info->next_device;
6078c2ecf20Sopenharmony_ci	if (sanity_check(info, tty->name, "open"))
6088c2ecf20Sopenharmony_ci		return -ENODEV;
6098c2ecf20Sopenharmony_ci	if (info->init_error) {
6108c2ecf20Sopenharmony_ci		DBGERR(("%s init error=%d\n", info->device_name, info->init_error));
6118c2ecf20Sopenharmony_ci		return -ENODEV;
6128c2ecf20Sopenharmony_ci	}
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	tty->driver_data = info;
6158c2ecf20Sopenharmony_ci	info->port.tty = tty;
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	DBGINFO(("%s open, old ref count = %d\n", info->device_name, info->port.count));
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	mutex_lock(&info->port.mutex);
6208c2ecf20Sopenharmony_ci	info->port.low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0;
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->netlock, flags);
6238c2ecf20Sopenharmony_ci	if (info->netcount) {
6248c2ecf20Sopenharmony_ci		retval = -EBUSY;
6258c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&info->netlock, flags);
6268c2ecf20Sopenharmony_ci		mutex_unlock(&info->port.mutex);
6278c2ecf20Sopenharmony_ci		goto cleanup;
6288c2ecf20Sopenharmony_ci	}
6298c2ecf20Sopenharmony_ci	info->port.count++;
6308c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->netlock, flags);
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	if (info->port.count == 1) {
6338c2ecf20Sopenharmony_ci		/* 1st open on this device, init hardware */
6348c2ecf20Sopenharmony_ci		retval = startup(info);
6358c2ecf20Sopenharmony_ci		if (retval < 0) {
6368c2ecf20Sopenharmony_ci			mutex_unlock(&info->port.mutex);
6378c2ecf20Sopenharmony_ci			goto cleanup;
6388c2ecf20Sopenharmony_ci		}
6398c2ecf20Sopenharmony_ci	}
6408c2ecf20Sopenharmony_ci	mutex_unlock(&info->port.mutex);
6418c2ecf20Sopenharmony_ci	retval = block_til_ready(tty, filp, info);
6428c2ecf20Sopenharmony_ci	if (retval) {
6438c2ecf20Sopenharmony_ci		DBGINFO(("%s block_til_ready rc=%d\n", info->device_name, retval));
6448c2ecf20Sopenharmony_ci		goto cleanup;
6458c2ecf20Sopenharmony_ci	}
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	retval = 0;
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_cicleanup:
6508c2ecf20Sopenharmony_ci	if (retval) {
6518c2ecf20Sopenharmony_ci		if (tty->count == 1)
6528c2ecf20Sopenharmony_ci			info->port.tty = NULL; /* tty layer will release tty struct */
6538c2ecf20Sopenharmony_ci		if(info->port.count)
6548c2ecf20Sopenharmony_ci			info->port.count--;
6558c2ecf20Sopenharmony_ci	}
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	DBGINFO(("%s open rc=%d\n", info->device_name, retval));
6588c2ecf20Sopenharmony_ci	return retval;
6598c2ecf20Sopenharmony_ci}
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_cistatic void close(struct tty_struct *tty, struct file *filp)
6628c2ecf20Sopenharmony_ci{
6638c2ecf20Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	if (sanity_check(info, tty->name, "close"))
6668c2ecf20Sopenharmony_ci		return;
6678c2ecf20Sopenharmony_ci	DBGINFO(("%s close entry, count=%d\n", info->device_name, info->port.count));
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	if (tty_port_close_start(&info->port, tty, filp) == 0)
6708c2ecf20Sopenharmony_ci		goto cleanup;
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	mutex_lock(&info->port.mutex);
6738c2ecf20Sopenharmony_ci	if (tty_port_initialized(&info->port))
6748c2ecf20Sopenharmony_ci 		wait_until_sent(tty, info->timeout);
6758c2ecf20Sopenharmony_ci	flush_buffer(tty);
6768c2ecf20Sopenharmony_ci	tty_ldisc_flush(tty);
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	shutdown(info);
6798c2ecf20Sopenharmony_ci	mutex_unlock(&info->port.mutex);
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	tty_port_close_end(&info->port, tty);
6828c2ecf20Sopenharmony_ci	info->port.tty = NULL;
6838c2ecf20Sopenharmony_cicleanup:
6848c2ecf20Sopenharmony_ci	DBGINFO(("%s close exit, count=%d\n", tty->driver->name, info->port.count));
6858c2ecf20Sopenharmony_ci}
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_cistatic void hangup(struct tty_struct *tty)
6888c2ecf20Sopenharmony_ci{
6898c2ecf20Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
6908c2ecf20Sopenharmony_ci	unsigned long flags;
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	if (sanity_check(info, tty->name, "hangup"))
6938c2ecf20Sopenharmony_ci		return;
6948c2ecf20Sopenharmony_ci	DBGINFO(("%s hangup\n", info->device_name));
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	flush_buffer(tty);
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	mutex_lock(&info->port.mutex);
6998c2ecf20Sopenharmony_ci	shutdown(info);
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->port.lock, flags);
7028c2ecf20Sopenharmony_ci	info->port.count = 0;
7038c2ecf20Sopenharmony_ci	info->port.tty = NULL;
7048c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->port.lock, flags);
7058c2ecf20Sopenharmony_ci	tty_port_set_active(&info->port, 0);
7068c2ecf20Sopenharmony_ci	mutex_unlock(&info->port.mutex);
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	wake_up_interruptible(&info->port.open_wait);
7098c2ecf20Sopenharmony_ci}
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_cistatic void set_termios(struct tty_struct *tty, struct ktermios *old_termios)
7128c2ecf20Sopenharmony_ci{
7138c2ecf20Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
7148c2ecf20Sopenharmony_ci	unsigned long flags;
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	DBGINFO(("%s set_termios\n", tty->driver->name));
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	change_params(info);
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	/* Handle transition to B0 status */
7218c2ecf20Sopenharmony_ci	if ((old_termios->c_cflag & CBAUD) && !C_BAUD(tty)) {
7228c2ecf20Sopenharmony_ci		info->signals &= ~(SerialSignal_RTS | SerialSignal_DTR);
7238c2ecf20Sopenharmony_ci		spin_lock_irqsave(&info->lock,flags);
7248c2ecf20Sopenharmony_ci		set_gtsignals(info);
7258c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&info->lock,flags);
7268c2ecf20Sopenharmony_ci	}
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	/* Handle transition away from B0 status */
7298c2ecf20Sopenharmony_ci	if (!(old_termios->c_cflag & CBAUD) && C_BAUD(tty)) {
7308c2ecf20Sopenharmony_ci		info->signals |= SerialSignal_DTR;
7318c2ecf20Sopenharmony_ci		if (!C_CRTSCTS(tty) || !tty_throttled(tty))
7328c2ecf20Sopenharmony_ci			info->signals |= SerialSignal_RTS;
7338c2ecf20Sopenharmony_ci		spin_lock_irqsave(&info->lock,flags);
7348c2ecf20Sopenharmony_ci	 	set_gtsignals(info);
7358c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&info->lock,flags);
7368c2ecf20Sopenharmony_ci	}
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci	/* Handle turning off CRTSCTS */
7398c2ecf20Sopenharmony_ci	if ((old_termios->c_cflag & CRTSCTS) && !C_CRTSCTS(tty)) {
7408c2ecf20Sopenharmony_ci		tty->hw_stopped = 0;
7418c2ecf20Sopenharmony_ci		tx_release(tty);
7428c2ecf20Sopenharmony_ci	}
7438c2ecf20Sopenharmony_ci}
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_cistatic void update_tx_timer(struct slgt_info *info)
7468c2ecf20Sopenharmony_ci{
7478c2ecf20Sopenharmony_ci	/*
7488c2ecf20Sopenharmony_ci	 * use worst case speed of 1200bps to calculate transmit timeout
7498c2ecf20Sopenharmony_ci	 * based on data in buffers (tbuf_bytes) and FIFO (128 bytes)
7508c2ecf20Sopenharmony_ci	 */
7518c2ecf20Sopenharmony_ci	if (info->params.mode == MGSL_MODE_HDLC) {
7528c2ecf20Sopenharmony_ci		int timeout  = (tbuf_bytes(info) * 7) + 1000;
7538c2ecf20Sopenharmony_ci		mod_timer(&info->tx_timer, jiffies + msecs_to_jiffies(timeout));
7548c2ecf20Sopenharmony_ci	}
7558c2ecf20Sopenharmony_ci}
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_cistatic int write(struct tty_struct *tty,
7588c2ecf20Sopenharmony_ci		 const unsigned char *buf, int count)
7598c2ecf20Sopenharmony_ci{
7608c2ecf20Sopenharmony_ci	int ret = 0;
7618c2ecf20Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
7628c2ecf20Sopenharmony_ci	unsigned long flags;
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	if (sanity_check(info, tty->name, "write"))
7658c2ecf20Sopenharmony_ci		return -EIO;
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	DBGINFO(("%s write count=%d\n", info->device_name, count));
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	if (!info->tx_buf || (count > info->max_frame_size))
7708c2ecf20Sopenharmony_ci		return -EIO;
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	if (!count || tty->stopped || tty->hw_stopped)
7738c2ecf20Sopenharmony_ci		return 0;
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->lock, flags);
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	if (info->tx_count) {
7788c2ecf20Sopenharmony_ci		/* send accumulated data from send_char() */
7798c2ecf20Sopenharmony_ci		if (!tx_load(info, info->tx_buf, info->tx_count))
7808c2ecf20Sopenharmony_ci			goto cleanup;
7818c2ecf20Sopenharmony_ci		info->tx_count = 0;
7828c2ecf20Sopenharmony_ci	}
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci	if (tx_load(info, buf, count))
7858c2ecf20Sopenharmony_ci		ret = count;
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_cicleanup:
7888c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->lock, flags);
7898c2ecf20Sopenharmony_ci	DBGINFO(("%s write rc=%d\n", info->device_name, ret));
7908c2ecf20Sopenharmony_ci	return ret;
7918c2ecf20Sopenharmony_ci}
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_cistatic int put_char(struct tty_struct *tty, unsigned char ch)
7948c2ecf20Sopenharmony_ci{
7958c2ecf20Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
7968c2ecf20Sopenharmony_ci	unsigned long flags;
7978c2ecf20Sopenharmony_ci	int ret = 0;
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	if (sanity_check(info, tty->name, "put_char"))
8008c2ecf20Sopenharmony_ci		return 0;
8018c2ecf20Sopenharmony_ci	DBGINFO(("%s put_char(%d)\n", info->device_name, ch));
8028c2ecf20Sopenharmony_ci	if (!info->tx_buf)
8038c2ecf20Sopenharmony_ci		return 0;
8048c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
8058c2ecf20Sopenharmony_ci	if (info->tx_count < info->max_frame_size) {
8068c2ecf20Sopenharmony_ci		info->tx_buf[info->tx_count++] = ch;
8078c2ecf20Sopenharmony_ci		ret = 1;
8088c2ecf20Sopenharmony_ci	}
8098c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
8108c2ecf20Sopenharmony_ci	return ret;
8118c2ecf20Sopenharmony_ci}
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_cistatic void send_xchar(struct tty_struct *tty, char ch)
8148c2ecf20Sopenharmony_ci{
8158c2ecf20Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
8168c2ecf20Sopenharmony_ci	unsigned long flags;
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	if (sanity_check(info, tty->name, "send_xchar"))
8198c2ecf20Sopenharmony_ci		return;
8208c2ecf20Sopenharmony_ci	DBGINFO(("%s send_xchar(%d)\n", info->device_name, ch));
8218c2ecf20Sopenharmony_ci	info->x_char = ch;
8228c2ecf20Sopenharmony_ci	if (ch) {
8238c2ecf20Sopenharmony_ci		spin_lock_irqsave(&info->lock,flags);
8248c2ecf20Sopenharmony_ci		if (!info->tx_enabled)
8258c2ecf20Sopenharmony_ci		 	tx_start(info);
8268c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&info->lock,flags);
8278c2ecf20Sopenharmony_ci	}
8288c2ecf20Sopenharmony_ci}
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_cistatic void wait_until_sent(struct tty_struct *tty, int timeout)
8318c2ecf20Sopenharmony_ci{
8328c2ecf20Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
8338c2ecf20Sopenharmony_ci	unsigned long orig_jiffies, char_time;
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	if (!info )
8368c2ecf20Sopenharmony_ci		return;
8378c2ecf20Sopenharmony_ci	if (sanity_check(info, tty->name, "wait_until_sent"))
8388c2ecf20Sopenharmony_ci		return;
8398c2ecf20Sopenharmony_ci	DBGINFO(("%s wait_until_sent entry\n", info->device_name));
8408c2ecf20Sopenharmony_ci	if (!tty_port_initialized(&info->port))
8418c2ecf20Sopenharmony_ci		goto exit;
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci	orig_jiffies = jiffies;
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	/* Set check interval to 1/5 of estimated time to
8468c2ecf20Sopenharmony_ci	 * send a character, and make it at least 1. The check
8478c2ecf20Sopenharmony_ci	 * interval should also be less than the timeout.
8488c2ecf20Sopenharmony_ci	 * Note: use tight timings here to satisfy the NIST-PCTS.
8498c2ecf20Sopenharmony_ci	 */
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	if (info->params.data_rate) {
8528c2ecf20Sopenharmony_ci	       	char_time = info->timeout/(32 * 5);
8538c2ecf20Sopenharmony_ci		if (!char_time)
8548c2ecf20Sopenharmony_ci			char_time++;
8558c2ecf20Sopenharmony_ci	} else
8568c2ecf20Sopenharmony_ci		char_time = 1;
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci	if (timeout)
8598c2ecf20Sopenharmony_ci		char_time = min_t(unsigned long, char_time, timeout);
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	while (info->tx_active) {
8628c2ecf20Sopenharmony_ci		msleep_interruptible(jiffies_to_msecs(char_time));
8638c2ecf20Sopenharmony_ci		if (signal_pending(current))
8648c2ecf20Sopenharmony_ci			break;
8658c2ecf20Sopenharmony_ci		if (timeout && time_after(jiffies, orig_jiffies + timeout))
8668c2ecf20Sopenharmony_ci			break;
8678c2ecf20Sopenharmony_ci	}
8688c2ecf20Sopenharmony_ciexit:
8698c2ecf20Sopenharmony_ci	DBGINFO(("%s wait_until_sent exit\n", info->device_name));
8708c2ecf20Sopenharmony_ci}
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_cistatic int write_room(struct tty_struct *tty)
8738c2ecf20Sopenharmony_ci{
8748c2ecf20Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
8758c2ecf20Sopenharmony_ci	int ret;
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	if (sanity_check(info, tty->name, "write_room"))
8788c2ecf20Sopenharmony_ci		return 0;
8798c2ecf20Sopenharmony_ci	ret = (info->tx_active) ? 0 : HDLC_MAX_FRAME_SIZE;
8808c2ecf20Sopenharmony_ci	DBGINFO(("%s write_room=%d\n", info->device_name, ret));
8818c2ecf20Sopenharmony_ci	return ret;
8828c2ecf20Sopenharmony_ci}
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_cistatic void flush_chars(struct tty_struct *tty)
8858c2ecf20Sopenharmony_ci{
8868c2ecf20Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
8878c2ecf20Sopenharmony_ci	unsigned long flags;
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci	if (sanity_check(info, tty->name, "flush_chars"))
8908c2ecf20Sopenharmony_ci		return;
8918c2ecf20Sopenharmony_ci	DBGINFO(("%s flush_chars entry tx_count=%d\n", info->device_name, info->tx_count));
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci	if (info->tx_count <= 0 || tty->stopped ||
8948c2ecf20Sopenharmony_ci	    tty->hw_stopped || !info->tx_buf)
8958c2ecf20Sopenharmony_ci		return;
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci	DBGINFO(("%s flush_chars start transmit\n", info->device_name));
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
9008c2ecf20Sopenharmony_ci	if (info->tx_count && tx_load(info, info->tx_buf, info->tx_count))
9018c2ecf20Sopenharmony_ci		info->tx_count = 0;
9028c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
9038c2ecf20Sopenharmony_ci}
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_cistatic void flush_buffer(struct tty_struct *tty)
9068c2ecf20Sopenharmony_ci{
9078c2ecf20Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
9088c2ecf20Sopenharmony_ci	unsigned long flags;
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci	if (sanity_check(info, tty->name, "flush_buffer"))
9118c2ecf20Sopenharmony_ci		return;
9128c2ecf20Sopenharmony_ci	DBGINFO(("%s flush_buffer\n", info->device_name));
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->lock, flags);
9158c2ecf20Sopenharmony_ci	info->tx_count = 0;
9168c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->lock, flags);
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_ci	tty_wakeup(tty);
9198c2ecf20Sopenharmony_ci}
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci/*
9228c2ecf20Sopenharmony_ci * throttle (stop) transmitter
9238c2ecf20Sopenharmony_ci */
9248c2ecf20Sopenharmony_cistatic void tx_hold(struct tty_struct *tty)
9258c2ecf20Sopenharmony_ci{
9268c2ecf20Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
9278c2ecf20Sopenharmony_ci	unsigned long flags;
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	if (sanity_check(info, tty->name, "tx_hold"))
9308c2ecf20Sopenharmony_ci		return;
9318c2ecf20Sopenharmony_ci	DBGINFO(("%s tx_hold\n", info->device_name));
9328c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
9338c2ecf20Sopenharmony_ci	if (info->tx_enabled && info->params.mode == MGSL_MODE_ASYNC)
9348c2ecf20Sopenharmony_ci	 	tx_stop(info);
9358c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
9368c2ecf20Sopenharmony_ci}
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci/*
9398c2ecf20Sopenharmony_ci * release (start) transmitter
9408c2ecf20Sopenharmony_ci */
9418c2ecf20Sopenharmony_cistatic void tx_release(struct tty_struct *tty)
9428c2ecf20Sopenharmony_ci{
9438c2ecf20Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
9448c2ecf20Sopenharmony_ci	unsigned long flags;
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	if (sanity_check(info, tty->name, "tx_release"))
9478c2ecf20Sopenharmony_ci		return;
9488c2ecf20Sopenharmony_ci	DBGINFO(("%s tx_release\n", info->device_name));
9498c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->lock, flags);
9508c2ecf20Sopenharmony_ci	if (info->tx_count && tx_load(info, info->tx_buf, info->tx_count))
9518c2ecf20Sopenharmony_ci		info->tx_count = 0;
9528c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->lock, flags);
9538c2ecf20Sopenharmony_ci}
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci/*
9568c2ecf20Sopenharmony_ci * Service an IOCTL request
9578c2ecf20Sopenharmony_ci *
9588c2ecf20Sopenharmony_ci * Arguments
9598c2ecf20Sopenharmony_ci *
9608c2ecf20Sopenharmony_ci * 	tty	pointer to tty instance data
9618c2ecf20Sopenharmony_ci * 	cmd	IOCTL command code
9628c2ecf20Sopenharmony_ci * 	arg	command argument/context
9638c2ecf20Sopenharmony_ci *
9648c2ecf20Sopenharmony_ci * Return 0 if success, otherwise error code
9658c2ecf20Sopenharmony_ci */
9668c2ecf20Sopenharmony_cistatic int ioctl(struct tty_struct *tty,
9678c2ecf20Sopenharmony_ci		 unsigned int cmd, unsigned long arg)
9688c2ecf20Sopenharmony_ci{
9698c2ecf20Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
9708c2ecf20Sopenharmony_ci	void __user *argp = (void __user *)arg;
9718c2ecf20Sopenharmony_ci	int ret;
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	if (sanity_check(info, tty->name, "ioctl"))
9748c2ecf20Sopenharmony_ci		return -ENODEV;
9758c2ecf20Sopenharmony_ci	DBGINFO(("%s ioctl() cmd=%08X\n", info->device_name, cmd));
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_ci	if (cmd != TIOCMIWAIT) {
9788c2ecf20Sopenharmony_ci		if (tty_io_error(tty))
9798c2ecf20Sopenharmony_ci		    return -EIO;
9808c2ecf20Sopenharmony_ci	}
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	switch (cmd) {
9838c2ecf20Sopenharmony_ci	case MGSL_IOCWAITEVENT:
9848c2ecf20Sopenharmony_ci		return wait_mgsl_event(info, argp);
9858c2ecf20Sopenharmony_ci	case TIOCMIWAIT:
9868c2ecf20Sopenharmony_ci		return modem_input_wait(info,(int)arg);
9878c2ecf20Sopenharmony_ci	case MGSL_IOCSGPIO:
9888c2ecf20Sopenharmony_ci		return set_gpio(info, argp);
9898c2ecf20Sopenharmony_ci	case MGSL_IOCGGPIO:
9908c2ecf20Sopenharmony_ci		return get_gpio(info, argp);
9918c2ecf20Sopenharmony_ci	case MGSL_IOCWAITGPIO:
9928c2ecf20Sopenharmony_ci		return wait_gpio(info, argp);
9938c2ecf20Sopenharmony_ci	case MGSL_IOCGXSYNC:
9948c2ecf20Sopenharmony_ci		return get_xsync(info, argp);
9958c2ecf20Sopenharmony_ci	case MGSL_IOCSXSYNC:
9968c2ecf20Sopenharmony_ci		return set_xsync(info, (int)arg);
9978c2ecf20Sopenharmony_ci	case MGSL_IOCGXCTRL:
9988c2ecf20Sopenharmony_ci		return get_xctrl(info, argp);
9998c2ecf20Sopenharmony_ci	case MGSL_IOCSXCTRL:
10008c2ecf20Sopenharmony_ci		return set_xctrl(info, (int)arg);
10018c2ecf20Sopenharmony_ci	}
10028c2ecf20Sopenharmony_ci	mutex_lock(&info->port.mutex);
10038c2ecf20Sopenharmony_ci	switch (cmd) {
10048c2ecf20Sopenharmony_ci	case MGSL_IOCGPARAMS:
10058c2ecf20Sopenharmony_ci		ret = get_params(info, argp);
10068c2ecf20Sopenharmony_ci		break;
10078c2ecf20Sopenharmony_ci	case MGSL_IOCSPARAMS:
10088c2ecf20Sopenharmony_ci		ret = set_params(info, argp);
10098c2ecf20Sopenharmony_ci		break;
10108c2ecf20Sopenharmony_ci	case MGSL_IOCGTXIDLE:
10118c2ecf20Sopenharmony_ci		ret = get_txidle(info, argp);
10128c2ecf20Sopenharmony_ci		break;
10138c2ecf20Sopenharmony_ci	case MGSL_IOCSTXIDLE:
10148c2ecf20Sopenharmony_ci		ret = set_txidle(info, (int)arg);
10158c2ecf20Sopenharmony_ci		break;
10168c2ecf20Sopenharmony_ci	case MGSL_IOCTXENABLE:
10178c2ecf20Sopenharmony_ci		ret = tx_enable(info, (int)arg);
10188c2ecf20Sopenharmony_ci		break;
10198c2ecf20Sopenharmony_ci	case MGSL_IOCRXENABLE:
10208c2ecf20Sopenharmony_ci		ret = rx_enable(info, (int)arg);
10218c2ecf20Sopenharmony_ci		break;
10228c2ecf20Sopenharmony_ci	case MGSL_IOCTXABORT:
10238c2ecf20Sopenharmony_ci		ret = tx_abort(info);
10248c2ecf20Sopenharmony_ci		break;
10258c2ecf20Sopenharmony_ci	case MGSL_IOCGSTATS:
10268c2ecf20Sopenharmony_ci		ret = get_stats(info, argp);
10278c2ecf20Sopenharmony_ci		break;
10288c2ecf20Sopenharmony_ci	case MGSL_IOCGIF:
10298c2ecf20Sopenharmony_ci		ret = get_interface(info, argp);
10308c2ecf20Sopenharmony_ci		break;
10318c2ecf20Sopenharmony_ci	case MGSL_IOCSIF:
10328c2ecf20Sopenharmony_ci		ret = set_interface(info,(int)arg);
10338c2ecf20Sopenharmony_ci		break;
10348c2ecf20Sopenharmony_ci	default:
10358c2ecf20Sopenharmony_ci		ret = -ENOIOCTLCMD;
10368c2ecf20Sopenharmony_ci	}
10378c2ecf20Sopenharmony_ci	mutex_unlock(&info->port.mutex);
10388c2ecf20Sopenharmony_ci	return ret;
10398c2ecf20Sopenharmony_ci}
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_cistatic int get_icount(struct tty_struct *tty,
10428c2ecf20Sopenharmony_ci				struct serial_icounter_struct *icount)
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_ci{
10458c2ecf20Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
10468c2ecf20Sopenharmony_ci	struct mgsl_icount cnow;	/* kernel counter temps */
10478c2ecf20Sopenharmony_ci	unsigned long flags;
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
10508c2ecf20Sopenharmony_ci	cnow = info->icount;
10518c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci	icount->cts = cnow.cts;
10548c2ecf20Sopenharmony_ci	icount->dsr = cnow.dsr;
10558c2ecf20Sopenharmony_ci	icount->rng = cnow.rng;
10568c2ecf20Sopenharmony_ci	icount->dcd = cnow.dcd;
10578c2ecf20Sopenharmony_ci	icount->rx = cnow.rx;
10588c2ecf20Sopenharmony_ci	icount->tx = cnow.tx;
10598c2ecf20Sopenharmony_ci	icount->frame = cnow.frame;
10608c2ecf20Sopenharmony_ci	icount->overrun = cnow.overrun;
10618c2ecf20Sopenharmony_ci	icount->parity = cnow.parity;
10628c2ecf20Sopenharmony_ci	icount->brk = cnow.brk;
10638c2ecf20Sopenharmony_ci	icount->buf_overrun = cnow.buf_overrun;
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_ci	return 0;
10668c2ecf20Sopenharmony_ci}
10678c2ecf20Sopenharmony_ci
10688c2ecf20Sopenharmony_ci/*
10698c2ecf20Sopenharmony_ci * support for 32 bit ioctl calls on 64 bit systems
10708c2ecf20Sopenharmony_ci */
10718c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT
10728c2ecf20Sopenharmony_cistatic long get_params32(struct slgt_info *info, struct MGSL_PARAMS32 __user *user_params)
10738c2ecf20Sopenharmony_ci{
10748c2ecf20Sopenharmony_ci	struct MGSL_PARAMS32 tmp_params;
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci	DBGINFO(("%s get_params32\n", info->device_name));
10778c2ecf20Sopenharmony_ci	memset(&tmp_params, 0, sizeof(tmp_params));
10788c2ecf20Sopenharmony_ci	tmp_params.mode            = (compat_ulong_t)info->params.mode;
10798c2ecf20Sopenharmony_ci	tmp_params.loopback        = info->params.loopback;
10808c2ecf20Sopenharmony_ci	tmp_params.flags           = info->params.flags;
10818c2ecf20Sopenharmony_ci	tmp_params.encoding        = info->params.encoding;
10828c2ecf20Sopenharmony_ci	tmp_params.clock_speed     = (compat_ulong_t)info->params.clock_speed;
10838c2ecf20Sopenharmony_ci	tmp_params.addr_filter     = info->params.addr_filter;
10848c2ecf20Sopenharmony_ci	tmp_params.crc_type        = info->params.crc_type;
10858c2ecf20Sopenharmony_ci	tmp_params.preamble_length = info->params.preamble_length;
10868c2ecf20Sopenharmony_ci	tmp_params.preamble        = info->params.preamble;
10878c2ecf20Sopenharmony_ci	tmp_params.data_rate       = (compat_ulong_t)info->params.data_rate;
10888c2ecf20Sopenharmony_ci	tmp_params.data_bits       = info->params.data_bits;
10898c2ecf20Sopenharmony_ci	tmp_params.stop_bits       = info->params.stop_bits;
10908c2ecf20Sopenharmony_ci	tmp_params.parity          = info->params.parity;
10918c2ecf20Sopenharmony_ci	if (copy_to_user(user_params, &tmp_params, sizeof(struct MGSL_PARAMS32)))
10928c2ecf20Sopenharmony_ci		return -EFAULT;
10938c2ecf20Sopenharmony_ci	return 0;
10948c2ecf20Sopenharmony_ci}
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_cistatic long set_params32(struct slgt_info *info, struct MGSL_PARAMS32 __user *new_params)
10978c2ecf20Sopenharmony_ci{
10988c2ecf20Sopenharmony_ci	struct MGSL_PARAMS32 tmp_params;
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_ci	DBGINFO(("%s set_params32\n", info->device_name));
11018c2ecf20Sopenharmony_ci	if (copy_from_user(&tmp_params, new_params, sizeof(struct MGSL_PARAMS32)))
11028c2ecf20Sopenharmony_ci		return -EFAULT;
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci	spin_lock(&info->lock);
11058c2ecf20Sopenharmony_ci	if (tmp_params.mode == MGSL_MODE_BASE_CLOCK) {
11068c2ecf20Sopenharmony_ci		info->base_clock = tmp_params.clock_speed;
11078c2ecf20Sopenharmony_ci	} else {
11088c2ecf20Sopenharmony_ci		info->params.mode            = tmp_params.mode;
11098c2ecf20Sopenharmony_ci		info->params.loopback        = tmp_params.loopback;
11108c2ecf20Sopenharmony_ci		info->params.flags           = tmp_params.flags;
11118c2ecf20Sopenharmony_ci		info->params.encoding        = tmp_params.encoding;
11128c2ecf20Sopenharmony_ci		info->params.clock_speed     = tmp_params.clock_speed;
11138c2ecf20Sopenharmony_ci		info->params.addr_filter     = tmp_params.addr_filter;
11148c2ecf20Sopenharmony_ci		info->params.crc_type        = tmp_params.crc_type;
11158c2ecf20Sopenharmony_ci		info->params.preamble_length = tmp_params.preamble_length;
11168c2ecf20Sopenharmony_ci		info->params.preamble        = tmp_params.preamble;
11178c2ecf20Sopenharmony_ci		info->params.data_rate       = tmp_params.data_rate;
11188c2ecf20Sopenharmony_ci		info->params.data_bits       = tmp_params.data_bits;
11198c2ecf20Sopenharmony_ci		info->params.stop_bits       = tmp_params.stop_bits;
11208c2ecf20Sopenharmony_ci		info->params.parity          = tmp_params.parity;
11218c2ecf20Sopenharmony_ci	}
11228c2ecf20Sopenharmony_ci	spin_unlock(&info->lock);
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci	program_hw(info);
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_ci	return 0;
11278c2ecf20Sopenharmony_ci}
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_cistatic long slgt_compat_ioctl(struct tty_struct *tty,
11308c2ecf20Sopenharmony_ci			 unsigned int cmd, unsigned long arg)
11318c2ecf20Sopenharmony_ci{
11328c2ecf20Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
11338c2ecf20Sopenharmony_ci	int rc;
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci	if (sanity_check(info, tty->name, "compat_ioctl"))
11368c2ecf20Sopenharmony_ci		return -ENODEV;
11378c2ecf20Sopenharmony_ci	DBGINFO(("%s compat_ioctl() cmd=%08X\n", info->device_name, cmd));
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci	switch (cmd) {
11408c2ecf20Sopenharmony_ci	case MGSL_IOCSPARAMS32:
11418c2ecf20Sopenharmony_ci		rc = set_params32(info, compat_ptr(arg));
11428c2ecf20Sopenharmony_ci		break;
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_ci	case MGSL_IOCGPARAMS32:
11458c2ecf20Sopenharmony_ci		rc = get_params32(info, compat_ptr(arg));
11468c2ecf20Sopenharmony_ci		break;
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_ci	case MGSL_IOCGPARAMS:
11498c2ecf20Sopenharmony_ci	case MGSL_IOCSPARAMS:
11508c2ecf20Sopenharmony_ci	case MGSL_IOCGTXIDLE:
11518c2ecf20Sopenharmony_ci	case MGSL_IOCGSTATS:
11528c2ecf20Sopenharmony_ci	case MGSL_IOCWAITEVENT:
11538c2ecf20Sopenharmony_ci	case MGSL_IOCGIF:
11548c2ecf20Sopenharmony_ci	case MGSL_IOCSGPIO:
11558c2ecf20Sopenharmony_ci	case MGSL_IOCGGPIO:
11568c2ecf20Sopenharmony_ci	case MGSL_IOCWAITGPIO:
11578c2ecf20Sopenharmony_ci	case MGSL_IOCGXSYNC:
11588c2ecf20Sopenharmony_ci	case MGSL_IOCGXCTRL:
11598c2ecf20Sopenharmony_ci		rc = ioctl(tty, cmd, (unsigned long)compat_ptr(arg));
11608c2ecf20Sopenharmony_ci		break;
11618c2ecf20Sopenharmony_ci	default:
11628c2ecf20Sopenharmony_ci		rc = ioctl(tty, cmd, arg);
11638c2ecf20Sopenharmony_ci	}
11648c2ecf20Sopenharmony_ci	DBGINFO(("%s compat_ioctl() cmd=%08X rc=%d\n", info->device_name, cmd, rc));
11658c2ecf20Sopenharmony_ci	return rc;
11668c2ecf20Sopenharmony_ci}
11678c2ecf20Sopenharmony_ci#else
11688c2ecf20Sopenharmony_ci#define slgt_compat_ioctl NULL
11698c2ecf20Sopenharmony_ci#endif /* ifdef CONFIG_COMPAT */
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci/*
11728c2ecf20Sopenharmony_ci * proc fs support
11738c2ecf20Sopenharmony_ci */
11748c2ecf20Sopenharmony_cistatic inline void line_info(struct seq_file *m, struct slgt_info *info)
11758c2ecf20Sopenharmony_ci{
11768c2ecf20Sopenharmony_ci	char stat_buf[30];
11778c2ecf20Sopenharmony_ci	unsigned long flags;
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_ci	seq_printf(m, "%s: IO=%08X IRQ=%d MaxFrameSize=%u\n",
11808c2ecf20Sopenharmony_ci		      info->device_name, info->phys_reg_addr,
11818c2ecf20Sopenharmony_ci		      info->irq_level, info->max_frame_size);
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci	/* output current serial signal states */
11848c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
11858c2ecf20Sopenharmony_ci	get_gtsignals(info);
11868c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_ci	stat_buf[0] = 0;
11898c2ecf20Sopenharmony_ci	stat_buf[1] = 0;
11908c2ecf20Sopenharmony_ci	if (info->signals & SerialSignal_RTS)
11918c2ecf20Sopenharmony_ci		strcat(stat_buf, "|RTS");
11928c2ecf20Sopenharmony_ci	if (info->signals & SerialSignal_CTS)
11938c2ecf20Sopenharmony_ci		strcat(stat_buf, "|CTS");
11948c2ecf20Sopenharmony_ci	if (info->signals & SerialSignal_DTR)
11958c2ecf20Sopenharmony_ci		strcat(stat_buf, "|DTR");
11968c2ecf20Sopenharmony_ci	if (info->signals & SerialSignal_DSR)
11978c2ecf20Sopenharmony_ci		strcat(stat_buf, "|DSR");
11988c2ecf20Sopenharmony_ci	if (info->signals & SerialSignal_DCD)
11998c2ecf20Sopenharmony_ci		strcat(stat_buf, "|CD");
12008c2ecf20Sopenharmony_ci	if (info->signals & SerialSignal_RI)
12018c2ecf20Sopenharmony_ci		strcat(stat_buf, "|RI");
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci	if (info->params.mode != MGSL_MODE_ASYNC) {
12048c2ecf20Sopenharmony_ci		seq_printf(m, "\tHDLC txok:%d rxok:%d",
12058c2ecf20Sopenharmony_ci			       info->icount.txok, info->icount.rxok);
12068c2ecf20Sopenharmony_ci		if (info->icount.txunder)
12078c2ecf20Sopenharmony_ci			seq_printf(m, " txunder:%d", info->icount.txunder);
12088c2ecf20Sopenharmony_ci		if (info->icount.txabort)
12098c2ecf20Sopenharmony_ci			seq_printf(m, " txabort:%d", info->icount.txabort);
12108c2ecf20Sopenharmony_ci		if (info->icount.rxshort)
12118c2ecf20Sopenharmony_ci			seq_printf(m, " rxshort:%d", info->icount.rxshort);
12128c2ecf20Sopenharmony_ci		if (info->icount.rxlong)
12138c2ecf20Sopenharmony_ci			seq_printf(m, " rxlong:%d", info->icount.rxlong);
12148c2ecf20Sopenharmony_ci		if (info->icount.rxover)
12158c2ecf20Sopenharmony_ci			seq_printf(m, " rxover:%d", info->icount.rxover);
12168c2ecf20Sopenharmony_ci		if (info->icount.rxcrc)
12178c2ecf20Sopenharmony_ci			seq_printf(m, " rxcrc:%d", info->icount.rxcrc);
12188c2ecf20Sopenharmony_ci	} else {
12198c2ecf20Sopenharmony_ci		seq_printf(m, "\tASYNC tx:%d rx:%d",
12208c2ecf20Sopenharmony_ci			       info->icount.tx, info->icount.rx);
12218c2ecf20Sopenharmony_ci		if (info->icount.frame)
12228c2ecf20Sopenharmony_ci			seq_printf(m, " fe:%d", info->icount.frame);
12238c2ecf20Sopenharmony_ci		if (info->icount.parity)
12248c2ecf20Sopenharmony_ci			seq_printf(m, " pe:%d", info->icount.parity);
12258c2ecf20Sopenharmony_ci		if (info->icount.brk)
12268c2ecf20Sopenharmony_ci			seq_printf(m, " brk:%d", info->icount.brk);
12278c2ecf20Sopenharmony_ci		if (info->icount.overrun)
12288c2ecf20Sopenharmony_ci			seq_printf(m, " oe:%d", info->icount.overrun);
12298c2ecf20Sopenharmony_ci	}
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_ci	/* Append serial signal status to end */
12328c2ecf20Sopenharmony_ci	seq_printf(m, " %s\n", stat_buf+1);
12338c2ecf20Sopenharmony_ci
12348c2ecf20Sopenharmony_ci	seq_printf(m, "\ttxactive=%d bh_req=%d bh_run=%d pending_bh=%x\n",
12358c2ecf20Sopenharmony_ci		       info->tx_active,info->bh_requested,info->bh_running,
12368c2ecf20Sopenharmony_ci		       info->pending_bh);
12378c2ecf20Sopenharmony_ci}
12388c2ecf20Sopenharmony_ci
12398c2ecf20Sopenharmony_ci/* Called to print information about devices
12408c2ecf20Sopenharmony_ci */
12418c2ecf20Sopenharmony_cistatic int synclink_gt_proc_show(struct seq_file *m, void *v)
12428c2ecf20Sopenharmony_ci{
12438c2ecf20Sopenharmony_ci	struct slgt_info *info;
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_ci	seq_puts(m, "synclink_gt driver\n");
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci	info = slgt_device_list;
12488c2ecf20Sopenharmony_ci	while( info ) {
12498c2ecf20Sopenharmony_ci		line_info(m, info);
12508c2ecf20Sopenharmony_ci		info = info->next_device;
12518c2ecf20Sopenharmony_ci	}
12528c2ecf20Sopenharmony_ci	return 0;
12538c2ecf20Sopenharmony_ci}
12548c2ecf20Sopenharmony_ci
12558c2ecf20Sopenharmony_ci/*
12568c2ecf20Sopenharmony_ci * return count of bytes in transmit buffer
12578c2ecf20Sopenharmony_ci */
12588c2ecf20Sopenharmony_cistatic int chars_in_buffer(struct tty_struct *tty)
12598c2ecf20Sopenharmony_ci{
12608c2ecf20Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
12618c2ecf20Sopenharmony_ci	int count;
12628c2ecf20Sopenharmony_ci	if (sanity_check(info, tty->name, "chars_in_buffer"))
12638c2ecf20Sopenharmony_ci		return 0;
12648c2ecf20Sopenharmony_ci	count = tbuf_bytes(info);
12658c2ecf20Sopenharmony_ci	DBGINFO(("%s chars_in_buffer()=%d\n", info->device_name, count));
12668c2ecf20Sopenharmony_ci	return count;
12678c2ecf20Sopenharmony_ci}
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_ci/*
12708c2ecf20Sopenharmony_ci * signal remote device to throttle send data (our receive data)
12718c2ecf20Sopenharmony_ci */
12728c2ecf20Sopenharmony_cistatic void throttle(struct tty_struct * tty)
12738c2ecf20Sopenharmony_ci{
12748c2ecf20Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
12758c2ecf20Sopenharmony_ci	unsigned long flags;
12768c2ecf20Sopenharmony_ci
12778c2ecf20Sopenharmony_ci	if (sanity_check(info, tty->name, "throttle"))
12788c2ecf20Sopenharmony_ci		return;
12798c2ecf20Sopenharmony_ci	DBGINFO(("%s throttle\n", info->device_name));
12808c2ecf20Sopenharmony_ci	if (I_IXOFF(tty))
12818c2ecf20Sopenharmony_ci		send_xchar(tty, STOP_CHAR(tty));
12828c2ecf20Sopenharmony_ci	if (C_CRTSCTS(tty)) {
12838c2ecf20Sopenharmony_ci		spin_lock_irqsave(&info->lock,flags);
12848c2ecf20Sopenharmony_ci		info->signals &= ~SerialSignal_RTS;
12858c2ecf20Sopenharmony_ci		set_gtsignals(info);
12868c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&info->lock,flags);
12878c2ecf20Sopenharmony_ci	}
12888c2ecf20Sopenharmony_ci}
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_ci/*
12918c2ecf20Sopenharmony_ci * signal remote device to stop throttling send data (our receive data)
12928c2ecf20Sopenharmony_ci */
12938c2ecf20Sopenharmony_cistatic void unthrottle(struct tty_struct * tty)
12948c2ecf20Sopenharmony_ci{
12958c2ecf20Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
12968c2ecf20Sopenharmony_ci	unsigned long flags;
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_ci	if (sanity_check(info, tty->name, "unthrottle"))
12998c2ecf20Sopenharmony_ci		return;
13008c2ecf20Sopenharmony_ci	DBGINFO(("%s unthrottle\n", info->device_name));
13018c2ecf20Sopenharmony_ci	if (I_IXOFF(tty)) {
13028c2ecf20Sopenharmony_ci		if (info->x_char)
13038c2ecf20Sopenharmony_ci			info->x_char = 0;
13048c2ecf20Sopenharmony_ci		else
13058c2ecf20Sopenharmony_ci			send_xchar(tty, START_CHAR(tty));
13068c2ecf20Sopenharmony_ci	}
13078c2ecf20Sopenharmony_ci	if (C_CRTSCTS(tty)) {
13088c2ecf20Sopenharmony_ci		spin_lock_irqsave(&info->lock,flags);
13098c2ecf20Sopenharmony_ci		info->signals |= SerialSignal_RTS;
13108c2ecf20Sopenharmony_ci		set_gtsignals(info);
13118c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&info->lock,flags);
13128c2ecf20Sopenharmony_ci	}
13138c2ecf20Sopenharmony_ci}
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_ci/*
13168c2ecf20Sopenharmony_ci * set or clear transmit break condition
13178c2ecf20Sopenharmony_ci * break_state	-1=set break condition, 0=clear
13188c2ecf20Sopenharmony_ci */
13198c2ecf20Sopenharmony_cistatic int set_break(struct tty_struct *tty, int break_state)
13208c2ecf20Sopenharmony_ci{
13218c2ecf20Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
13228c2ecf20Sopenharmony_ci	unsigned short value;
13238c2ecf20Sopenharmony_ci	unsigned long flags;
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_ci	if (sanity_check(info, tty->name, "set_break"))
13268c2ecf20Sopenharmony_ci		return -EINVAL;
13278c2ecf20Sopenharmony_ci	DBGINFO(("%s set_break(%d)\n", info->device_name, break_state));
13288c2ecf20Sopenharmony_ci
13298c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
13308c2ecf20Sopenharmony_ci	value = rd_reg16(info, TCR);
13318c2ecf20Sopenharmony_ci 	if (break_state == -1)
13328c2ecf20Sopenharmony_ci		value |= BIT6;
13338c2ecf20Sopenharmony_ci	else
13348c2ecf20Sopenharmony_ci		value &= ~BIT6;
13358c2ecf20Sopenharmony_ci	wr_reg16(info, TCR, value);
13368c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
13378c2ecf20Sopenharmony_ci	return 0;
13388c2ecf20Sopenharmony_ci}
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_ci#if SYNCLINK_GENERIC_HDLC
13418c2ecf20Sopenharmony_ci
13428c2ecf20Sopenharmony_ci/**
13438c2ecf20Sopenharmony_ci * hdlcdev_attach - called by generic HDLC layer when protocol selected (PPP, frame relay, etc.)
13448c2ecf20Sopenharmony_ci * @dev:      pointer to network device structure
13458c2ecf20Sopenharmony_ci * @encoding: serial encoding setting
13468c2ecf20Sopenharmony_ci * @parity:   FCS setting
13478c2ecf20Sopenharmony_ci *
13488c2ecf20Sopenharmony_ci * Set encoding and frame check sequence (FCS) options.
13498c2ecf20Sopenharmony_ci *
13508c2ecf20Sopenharmony_ci * Return: 0 if success, otherwise error code
13518c2ecf20Sopenharmony_ci */
13528c2ecf20Sopenharmony_cistatic int hdlcdev_attach(struct net_device *dev, unsigned short encoding,
13538c2ecf20Sopenharmony_ci			  unsigned short parity)
13548c2ecf20Sopenharmony_ci{
13558c2ecf20Sopenharmony_ci	struct slgt_info *info = dev_to_port(dev);
13568c2ecf20Sopenharmony_ci	unsigned char  new_encoding;
13578c2ecf20Sopenharmony_ci	unsigned short new_crctype;
13588c2ecf20Sopenharmony_ci
13598c2ecf20Sopenharmony_ci	/* return error if TTY interface open */
13608c2ecf20Sopenharmony_ci	if (info->port.count)
13618c2ecf20Sopenharmony_ci		return -EBUSY;
13628c2ecf20Sopenharmony_ci
13638c2ecf20Sopenharmony_ci	DBGINFO(("%s hdlcdev_attach\n", info->device_name));
13648c2ecf20Sopenharmony_ci
13658c2ecf20Sopenharmony_ci	switch (encoding)
13668c2ecf20Sopenharmony_ci	{
13678c2ecf20Sopenharmony_ci	case ENCODING_NRZ:        new_encoding = HDLC_ENCODING_NRZ; break;
13688c2ecf20Sopenharmony_ci	case ENCODING_NRZI:       new_encoding = HDLC_ENCODING_NRZI_SPACE; break;
13698c2ecf20Sopenharmony_ci	case ENCODING_FM_MARK:    new_encoding = HDLC_ENCODING_BIPHASE_MARK; break;
13708c2ecf20Sopenharmony_ci	case ENCODING_FM_SPACE:   new_encoding = HDLC_ENCODING_BIPHASE_SPACE; break;
13718c2ecf20Sopenharmony_ci	case ENCODING_MANCHESTER: new_encoding = HDLC_ENCODING_BIPHASE_LEVEL; break;
13728c2ecf20Sopenharmony_ci	default: return -EINVAL;
13738c2ecf20Sopenharmony_ci	}
13748c2ecf20Sopenharmony_ci
13758c2ecf20Sopenharmony_ci	switch (parity)
13768c2ecf20Sopenharmony_ci	{
13778c2ecf20Sopenharmony_ci	case PARITY_NONE:            new_crctype = HDLC_CRC_NONE; break;
13788c2ecf20Sopenharmony_ci	case PARITY_CRC16_PR1_CCITT: new_crctype = HDLC_CRC_16_CCITT; break;
13798c2ecf20Sopenharmony_ci	case PARITY_CRC32_PR1_CCITT: new_crctype = HDLC_CRC_32_CCITT; break;
13808c2ecf20Sopenharmony_ci	default: return -EINVAL;
13818c2ecf20Sopenharmony_ci	}
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_ci	info->params.encoding = new_encoding;
13848c2ecf20Sopenharmony_ci	info->params.crc_type = new_crctype;
13858c2ecf20Sopenharmony_ci
13868c2ecf20Sopenharmony_ci	/* if network interface up, reprogram hardware */
13878c2ecf20Sopenharmony_ci	if (info->netcount)
13888c2ecf20Sopenharmony_ci		program_hw(info);
13898c2ecf20Sopenharmony_ci
13908c2ecf20Sopenharmony_ci	return 0;
13918c2ecf20Sopenharmony_ci}
13928c2ecf20Sopenharmony_ci
13938c2ecf20Sopenharmony_ci/**
13948c2ecf20Sopenharmony_ci * hdlcdev_xmit - called by generic HDLC layer to send a frame
13958c2ecf20Sopenharmony_ci * @skb: socket buffer containing HDLC frame
13968c2ecf20Sopenharmony_ci * @dev: pointer to network device structure
13978c2ecf20Sopenharmony_ci */
13988c2ecf20Sopenharmony_cistatic netdev_tx_t hdlcdev_xmit(struct sk_buff *skb,
13998c2ecf20Sopenharmony_ci				      struct net_device *dev)
14008c2ecf20Sopenharmony_ci{
14018c2ecf20Sopenharmony_ci	struct slgt_info *info = dev_to_port(dev);
14028c2ecf20Sopenharmony_ci	unsigned long flags;
14038c2ecf20Sopenharmony_ci
14048c2ecf20Sopenharmony_ci	DBGINFO(("%s hdlc_xmit\n", dev->name));
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_ci	if (!skb->len)
14078c2ecf20Sopenharmony_ci		return NETDEV_TX_OK;
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ci	/* stop sending until this frame completes */
14108c2ecf20Sopenharmony_ci	netif_stop_queue(dev);
14118c2ecf20Sopenharmony_ci
14128c2ecf20Sopenharmony_ci	/* update network statistics */
14138c2ecf20Sopenharmony_ci	dev->stats.tx_packets++;
14148c2ecf20Sopenharmony_ci	dev->stats.tx_bytes += skb->len;
14158c2ecf20Sopenharmony_ci
14168c2ecf20Sopenharmony_ci	/* save start time for transmit timeout detection */
14178c2ecf20Sopenharmony_ci	netif_trans_update(dev);
14188c2ecf20Sopenharmony_ci
14198c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->lock, flags);
14208c2ecf20Sopenharmony_ci	tx_load(info, skb->data, skb->len);
14218c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->lock, flags);
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_ci	/* done with socket buffer, so free it */
14248c2ecf20Sopenharmony_ci	dev_kfree_skb(skb);
14258c2ecf20Sopenharmony_ci
14268c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
14278c2ecf20Sopenharmony_ci}
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_ci/**
14308c2ecf20Sopenharmony_ci * hdlcdev_open - called by network layer when interface enabled
14318c2ecf20Sopenharmony_ci * @dev: pointer to network device structure
14328c2ecf20Sopenharmony_ci *
14338c2ecf20Sopenharmony_ci * Claim resources and initialize hardware.
14348c2ecf20Sopenharmony_ci *
14358c2ecf20Sopenharmony_ci * Return: 0 if success, otherwise error code
14368c2ecf20Sopenharmony_ci */
14378c2ecf20Sopenharmony_cistatic int hdlcdev_open(struct net_device *dev)
14388c2ecf20Sopenharmony_ci{
14398c2ecf20Sopenharmony_ci	struct slgt_info *info = dev_to_port(dev);
14408c2ecf20Sopenharmony_ci	int rc;
14418c2ecf20Sopenharmony_ci	unsigned long flags;
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_ci	if (!try_module_get(THIS_MODULE))
14448c2ecf20Sopenharmony_ci		return -EBUSY;
14458c2ecf20Sopenharmony_ci
14468c2ecf20Sopenharmony_ci	DBGINFO(("%s hdlcdev_open\n", dev->name));
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_ci	/* generic HDLC layer open processing */
14498c2ecf20Sopenharmony_ci	rc = hdlc_open(dev);
14508c2ecf20Sopenharmony_ci	if (rc)
14518c2ecf20Sopenharmony_ci		return rc;
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_ci	/* arbitrate between network and tty opens */
14548c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->netlock, flags);
14558c2ecf20Sopenharmony_ci	if (info->port.count != 0 || info->netcount != 0) {
14568c2ecf20Sopenharmony_ci		DBGINFO(("%s hdlc_open busy\n", dev->name));
14578c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&info->netlock, flags);
14588c2ecf20Sopenharmony_ci		return -EBUSY;
14598c2ecf20Sopenharmony_ci	}
14608c2ecf20Sopenharmony_ci	info->netcount=1;
14618c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->netlock, flags);
14628c2ecf20Sopenharmony_ci
14638c2ecf20Sopenharmony_ci	/* claim resources and init adapter */
14648c2ecf20Sopenharmony_ci	if ((rc = startup(info)) != 0) {
14658c2ecf20Sopenharmony_ci		spin_lock_irqsave(&info->netlock, flags);
14668c2ecf20Sopenharmony_ci		info->netcount=0;
14678c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&info->netlock, flags);
14688c2ecf20Sopenharmony_ci		return rc;
14698c2ecf20Sopenharmony_ci	}
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_ci	/* assert RTS and DTR, apply hardware settings */
14728c2ecf20Sopenharmony_ci	info->signals |= SerialSignal_RTS | SerialSignal_DTR;
14738c2ecf20Sopenharmony_ci	program_hw(info);
14748c2ecf20Sopenharmony_ci
14758c2ecf20Sopenharmony_ci	/* enable network layer transmit */
14768c2ecf20Sopenharmony_ci	netif_trans_update(dev);
14778c2ecf20Sopenharmony_ci	netif_start_queue(dev);
14788c2ecf20Sopenharmony_ci
14798c2ecf20Sopenharmony_ci	/* inform generic HDLC layer of current DCD status */
14808c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->lock, flags);
14818c2ecf20Sopenharmony_ci	get_gtsignals(info);
14828c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->lock, flags);
14838c2ecf20Sopenharmony_ci	if (info->signals & SerialSignal_DCD)
14848c2ecf20Sopenharmony_ci		netif_carrier_on(dev);
14858c2ecf20Sopenharmony_ci	else
14868c2ecf20Sopenharmony_ci		netif_carrier_off(dev);
14878c2ecf20Sopenharmony_ci	return 0;
14888c2ecf20Sopenharmony_ci}
14898c2ecf20Sopenharmony_ci
14908c2ecf20Sopenharmony_ci/**
14918c2ecf20Sopenharmony_ci * hdlcdev_close - called by network layer when interface is disabled
14928c2ecf20Sopenharmony_ci * @dev:  pointer to network device structure
14938c2ecf20Sopenharmony_ci *
14948c2ecf20Sopenharmony_ci * Shutdown hardware and release resources.
14958c2ecf20Sopenharmony_ci *
14968c2ecf20Sopenharmony_ci * Return: 0 if success, otherwise error code
14978c2ecf20Sopenharmony_ci */
14988c2ecf20Sopenharmony_cistatic int hdlcdev_close(struct net_device *dev)
14998c2ecf20Sopenharmony_ci{
15008c2ecf20Sopenharmony_ci	struct slgt_info *info = dev_to_port(dev);
15018c2ecf20Sopenharmony_ci	unsigned long flags;
15028c2ecf20Sopenharmony_ci
15038c2ecf20Sopenharmony_ci	DBGINFO(("%s hdlcdev_close\n", dev->name));
15048c2ecf20Sopenharmony_ci
15058c2ecf20Sopenharmony_ci	netif_stop_queue(dev);
15068c2ecf20Sopenharmony_ci
15078c2ecf20Sopenharmony_ci	/* shutdown adapter and release resources */
15088c2ecf20Sopenharmony_ci	shutdown(info);
15098c2ecf20Sopenharmony_ci
15108c2ecf20Sopenharmony_ci	hdlc_close(dev);
15118c2ecf20Sopenharmony_ci
15128c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->netlock, flags);
15138c2ecf20Sopenharmony_ci	info->netcount=0;
15148c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->netlock, flags);
15158c2ecf20Sopenharmony_ci
15168c2ecf20Sopenharmony_ci	module_put(THIS_MODULE);
15178c2ecf20Sopenharmony_ci	return 0;
15188c2ecf20Sopenharmony_ci}
15198c2ecf20Sopenharmony_ci
15208c2ecf20Sopenharmony_ci/**
15218c2ecf20Sopenharmony_ci * hdlcdev_ioctl - called by network layer to process IOCTL call to network device
15228c2ecf20Sopenharmony_ci * @dev: pointer to network device structure
15238c2ecf20Sopenharmony_ci * @ifr: pointer to network interface request structure
15248c2ecf20Sopenharmony_ci * @cmd: IOCTL command code
15258c2ecf20Sopenharmony_ci *
15268c2ecf20Sopenharmony_ci * Return: 0 if success, otherwise error code
15278c2ecf20Sopenharmony_ci */
15288c2ecf20Sopenharmony_cistatic int hdlcdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
15298c2ecf20Sopenharmony_ci{
15308c2ecf20Sopenharmony_ci	const size_t size = sizeof(sync_serial_settings);
15318c2ecf20Sopenharmony_ci	sync_serial_settings new_line;
15328c2ecf20Sopenharmony_ci	sync_serial_settings __user *line = ifr->ifr_settings.ifs_ifsu.sync;
15338c2ecf20Sopenharmony_ci	struct slgt_info *info = dev_to_port(dev);
15348c2ecf20Sopenharmony_ci	unsigned int flags;
15358c2ecf20Sopenharmony_ci
15368c2ecf20Sopenharmony_ci	DBGINFO(("%s hdlcdev_ioctl\n", dev->name));
15378c2ecf20Sopenharmony_ci
15388c2ecf20Sopenharmony_ci	/* return error if TTY interface open */
15398c2ecf20Sopenharmony_ci	if (info->port.count)
15408c2ecf20Sopenharmony_ci		return -EBUSY;
15418c2ecf20Sopenharmony_ci
15428c2ecf20Sopenharmony_ci	if (cmd != SIOCWANDEV)
15438c2ecf20Sopenharmony_ci		return hdlc_ioctl(dev, ifr, cmd);
15448c2ecf20Sopenharmony_ci
15458c2ecf20Sopenharmony_ci	memset(&new_line, 0, sizeof(new_line));
15468c2ecf20Sopenharmony_ci
15478c2ecf20Sopenharmony_ci	switch(ifr->ifr_settings.type) {
15488c2ecf20Sopenharmony_ci	case IF_GET_IFACE: /* return current sync_serial_settings */
15498c2ecf20Sopenharmony_ci
15508c2ecf20Sopenharmony_ci		ifr->ifr_settings.type = IF_IFACE_SYNC_SERIAL;
15518c2ecf20Sopenharmony_ci		if (ifr->ifr_settings.size < size) {
15528c2ecf20Sopenharmony_ci			ifr->ifr_settings.size = size; /* data size wanted */
15538c2ecf20Sopenharmony_ci			return -ENOBUFS;
15548c2ecf20Sopenharmony_ci		}
15558c2ecf20Sopenharmony_ci
15568c2ecf20Sopenharmony_ci		flags = info->params.flags & (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL |
15578c2ecf20Sopenharmony_ci					      HDLC_FLAG_RXC_BRG    | HDLC_FLAG_RXC_TXCPIN |
15588c2ecf20Sopenharmony_ci					      HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL |
15598c2ecf20Sopenharmony_ci					      HDLC_FLAG_TXC_BRG    | HDLC_FLAG_TXC_RXCPIN);
15608c2ecf20Sopenharmony_ci
15618c2ecf20Sopenharmony_ci		switch (flags){
15628c2ecf20Sopenharmony_ci		case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN): new_line.clock_type = CLOCK_EXT; break;
15638c2ecf20Sopenharmony_ci		case (HDLC_FLAG_RXC_BRG    | HDLC_FLAG_TXC_BRG):    new_line.clock_type = CLOCK_INT; break;
15648c2ecf20Sopenharmony_ci		case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG):    new_line.clock_type = CLOCK_TXINT; break;
15658c2ecf20Sopenharmony_ci		case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN): new_line.clock_type = CLOCK_TXFROMRX; break;
15668c2ecf20Sopenharmony_ci		default: new_line.clock_type = CLOCK_DEFAULT;
15678c2ecf20Sopenharmony_ci		}
15688c2ecf20Sopenharmony_ci
15698c2ecf20Sopenharmony_ci		new_line.clock_rate = info->params.clock_speed;
15708c2ecf20Sopenharmony_ci		new_line.loopback   = info->params.loopback ? 1:0;
15718c2ecf20Sopenharmony_ci
15728c2ecf20Sopenharmony_ci		if (copy_to_user(line, &new_line, size))
15738c2ecf20Sopenharmony_ci			return -EFAULT;
15748c2ecf20Sopenharmony_ci		return 0;
15758c2ecf20Sopenharmony_ci
15768c2ecf20Sopenharmony_ci	case IF_IFACE_SYNC_SERIAL: /* set sync_serial_settings */
15778c2ecf20Sopenharmony_ci
15788c2ecf20Sopenharmony_ci		if(!capable(CAP_NET_ADMIN))
15798c2ecf20Sopenharmony_ci			return -EPERM;
15808c2ecf20Sopenharmony_ci		if (copy_from_user(&new_line, line, size))
15818c2ecf20Sopenharmony_ci			return -EFAULT;
15828c2ecf20Sopenharmony_ci
15838c2ecf20Sopenharmony_ci		switch (new_line.clock_type)
15848c2ecf20Sopenharmony_ci		{
15858c2ecf20Sopenharmony_ci		case CLOCK_EXT:      flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN; break;
15868c2ecf20Sopenharmony_ci		case CLOCK_TXFROMRX: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN; break;
15878c2ecf20Sopenharmony_ci		case CLOCK_INT:      flags = HDLC_FLAG_RXC_BRG    | HDLC_FLAG_TXC_BRG;    break;
15888c2ecf20Sopenharmony_ci		case CLOCK_TXINT:    flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG;    break;
15898c2ecf20Sopenharmony_ci		case CLOCK_DEFAULT:  flags = info->params.flags &
15908c2ecf20Sopenharmony_ci					     (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL |
15918c2ecf20Sopenharmony_ci					      HDLC_FLAG_RXC_BRG    | HDLC_FLAG_RXC_TXCPIN |
15928c2ecf20Sopenharmony_ci					      HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL |
15938c2ecf20Sopenharmony_ci					      HDLC_FLAG_TXC_BRG    | HDLC_FLAG_TXC_RXCPIN); break;
15948c2ecf20Sopenharmony_ci		default: return -EINVAL;
15958c2ecf20Sopenharmony_ci		}
15968c2ecf20Sopenharmony_ci
15978c2ecf20Sopenharmony_ci		if (new_line.loopback != 0 && new_line.loopback != 1)
15988c2ecf20Sopenharmony_ci			return -EINVAL;
15998c2ecf20Sopenharmony_ci
16008c2ecf20Sopenharmony_ci		info->params.flags &= ~(HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL |
16018c2ecf20Sopenharmony_ci					HDLC_FLAG_RXC_BRG    | HDLC_FLAG_RXC_TXCPIN |
16028c2ecf20Sopenharmony_ci					HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL |
16038c2ecf20Sopenharmony_ci					HDLC_FLAG_TXC_BRG    | HDLC_FLAG_TXC_RXCPIN);
16048c2ecf20Sopenharmony_ci		info->params.flags |= flags;
16058c2ecf20Sopenharmony_ci
16068c2ecf20Sopenharmony_ci		info->params.loopback = new_line.loopback;
16078c2ecf20Sopenharmony_ci
16088c2ecf20Sopenharmony_ci		if (flags & (HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG))
16098c2ecf20Sopenharmony_ci			info->params.clock_speed = new_line.clock_rate;
16108c2ecf20Sopenharmony_ci		else
16118c2ecf20Sopenharmony_ci			info->params.clock_speed = 0;
16128c2ecf20Sopenharmony_ci
16138c2ecf20Sopenharmony_ci		/* if network interface up, reprogram hardware */
16148c2ecf20Sopenharmony_ci		if (info->netcount)
16158c2ecf20Sopenharmony_ci			program_hw(info);
16168c2ecf20Sopenharmony_ci		return 0;
16178c2ecf20Sopenharmony_ci
16188c2ecf20Sopenharmony_ci	default:
16198c2ecf20Sopenharmony_ci		return hdlc_ioctl(dev, ifr, cmd);
16208c2ecf20Sopenharmony_ci	}
16218c2ecf20Sopenharmony_ci}
16228c2ecf20Sopenharmony_ci
16238c2ecf20Sopenharmony_ci/**
16248c2ecf20Sopenharmony_ci * hdlcdev_tx_timeout - called by network layer when transmit timeout is detected
16258c2ecf20Sopenharmony_ci * @dev: pointer to network device structure
16268c2ecf20Sopenharmony_ci */
16278c2ecf20Sopenharmony_cistatic void hdlcdev_tx_timeout(struct net_device *dev, unsigned int txqueue)
16288c2ecf20Sopenharmony_ci{
16298c2ecf20Sopenharmony_ci	struct slgt_info *info = dev_to_port(dev);
16308c2ecf20Sopenharmony_ci	unsigned long flags;
16318c2ecf20Sopenharmony_ci
16328c2ecf20Sopenharmony_ci	DBGINFO(("%s hdlcdev_tx_timeout\n", dev->name));
16338c2ecf20Sopenharmony_ci
16348c2ecf20Sopenharmony_ci	dev->stats.tx_errors++;
16358c2ecf20Sopenharmony_ci	dev->stats.tx_aborted_errors++;
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
16388c2ecf20Sopenharmony_ci	tx_stop(info);
16398c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
16408c2ecf20Sopenharmony_ci
16418c2ecf20Sopenharmony_ci	netif_wake_queue(dev);
16428c2ecf20Sopenharmony_ci}
16438c2ecf20Sopenharmony_ci
16448c2ecf20Sopenharmony_ci/**
16458c2ecf20Sopenharmony_ci * hdlcdev_tx_done - called by device driver when transmit completes
16468c2ecf20Sopenharmony_ci * @info: pointer to device instance information
16478c2ecf20Sopenharmony_ci *
16488c2ecf20Sopenharmony_ci * Reenable network layer transmit if stopped.
16498c2ecf20Sopenharmony_ci */
16508c2ecf20Sopenharmony_cistatic void hdlcdev_tx_done(struct slgt_info *info)
16518c2ecf20Sopenharmony_ci{
16528c2ecf20Sopenharmony_ci	if (netif_queue_stopped(info->netdev))
16538c2ecf20Sopenharmony_ci		netif_wake_queue(info->netdev);
16548c2ecf20Sopenharmony_ci}
16558c2ecf20Sopenharmony_ci
16568c2ecf20Sopenharmony_ci/**
16578c2ecf20Sopenharmony_ci * hdlcdev_rx - called by device driver when frame received
16588c2ecf20Sopenharmony_ci * @info: pointer to device instance information
16598c2ecf20Sopenharmony_ci * @buf:  pointer to buffer contianing frame data
16608c2ecf20Sopenharmony_ci * @size: count of data bytes in buf
16618c2ecf20Sopenharmony_ci *
16628c2ecf20Sopenharmony_ci * Pass frame to network layer.
16638c2ecf20Sopenharmony_ci */
16648c2ecf20Sopenharmony_cistatic void hdlcdev_rx(struct slgt_info *info, char *buf, int size)
16658c2ecf20Sopenharmony_ci{
16668c2ecf20Sopenharmony_ci	struct sk_buff *skb = dev_alloc_skb(size);
16678c2ecf20Sopenharmony_ci	struct net_device *dev = info->netdev;
16688c2ecf20Sopenharmony_ci
16698c2ecf20Sopenharmony_ci	DBGINFO(("%s hdlcdev_rx\n", dev->name));
16708c2ecf20Sopenharmony_ci
16718c2ecf20Sopenharmony_ci	if (skb == NULL) {
16728c2ecf20Sopenharmony_ci		DBGERR(("%s: can't alloc skb, drop packet\n", dev->name));
16738c2ecf20Sopenharmony_ci		dev->stats.rx_dropped++;
16748c2ecf20Sopenharmony_ci		return;
16758c2ecf20Sopenharmony_ci	}
16768c2ecf20Sopenharmony_ci
16778c2ecf20Sopenharmony_ci	skb_put_data(skb, buf, size);
16788c2ecf20Sopenharmony_ci
16798c2ecf20Sopenharmony_ci	skb->protocol = hdlc_type_trans(skb, dev);
16808c2ecf20Sopenharmony_ci
16818c2ecf20Sopenharmony_ci	dev->stats.rx_packets++;
16828c2ecf20Sopenharmony_ci	dev->stats.rx_bytes += size;
16838c2ecf20Sopenharmony_ci
16848c2ecf20Sopenharmony_ci	netif_rx(skb);
16858c2ecf20Sopenharmony_ci}
16868c2ecf20Sopenharmony_ci
16878c2ecf20Sopenharmony_cistatic const struct net_device_ops hdlcdev_ops = {
16888c2ecf20Sopenharmony_ci	.ndo_open       = hdlcdev_open,
16898c2ecf20Sopenharmony_ci	.ndo_stop       = hdlcdev_close,
16908c2ecf20Sopenharmony_ci	.ndo_start_xmit = hdlc_start_xmit,
16918c2ecf20Sopenharmony_ci	.ndo_do_ioctl   = hdlcdev_ioctl,
16928c2ecf20Sopenharmony_ci	.ndo_tx_timeout = hdlcdev_tx_timeout,
16938c2ecf20Sopenharmony_ci};
16948c2ecf20Sopenharmony_ci
16958c2ecf20Sopenharmony_ci/**
16968c2ecf20Sopenharmony_ci * hdlcdev_init - called by device driver when adding device instance
16978c2ecf20Sopenharmony_ci * @info: pointer to device instance information
16988c2ecf20Sopenharmony_ci *
16998c2ecf20Sopenharmony_ci * Do generic HDLC initialization.
17008c2ecf20Sopenharmony_ci *
17018c2ecf20Sopenharmony_ci * Return: 0 if success, otherwise error code
17028c2ecf20Sopenharmony_ci */
17038c2ecf20Sopenharmony_cistatic int hdlcdev_init(struct slgt_info *info)
17048c2ecf20Sopenharmony_ci{
17058c2ecf20Sopenharmony_ci	int rc;
17068c2ecf20Sopenharmony_ci	struct net_device *dev;
17078c2ecf20Sopenharmony_ci	hdlc_device *hdlc;
17088c2ecf20Sopenharmony_ci
17098c2ecf20Sopenharmony_ci	/* allocate and initialize network and HDLC layer objects */
17108c2ecf20Sopenharmony_ci
17118c2ecf20Sopenharmony_ci	dev = alloc_hdlcdev(info);
17128c2ecf20Sopenharmony_ci	if (!dev) {
17138c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s hdlc device alloc failure\n", info->device_name);
17148c2ecf20Sopenharmony_ci		return -ENOMEM;
17158c2ecf20Sopenharmony_ci	}
17168c2ecf20Sopenharmony_ci
17178c2ecf20Sopenharmony_ci	/* for network layer reporting purposes only */
17188c2ecf20Sopenharmony_ci	dev->mem_start = info->phys_reg_addr;
17198c2ecf20Sopenharmony_ci	dev->mem_end   = info->phys_reg_addr + SLGT_REG_SIZE - 1;
17208c2ecf20Sopenharmony_ci	dev->irq       = info->irq_level;
17218c2ecf20Sopenharmony_ci
17228c2ecf20Sopenharmony_ci	/* network layer callbacks and settings */
17238c2ecf20Sopenharmony_ci	dev->netdev_ops	    = &hdlcdev_ops;
17248c2ecf20Sopenharmony_ci	dev->watchdog_timeo = 10 * HZ;
17258c2ecf20Sopenharmony_ci	dev->tx_queue_len   = 50;
17268c2ecf20Sopenharmony_ci
17278c2ecf20Sopenharmony_ci	/* generic HDLC layer callbacks and settings */
17288c2ecf20Sopenharmony_ci	hdlc         = dev_to_hdlc(dev);
17298c2ecf20Sopenharmony_ci	hdlc->attach = hdlcdev_attach;
17308c2ecf20Sopenharmony_ci	hdlc->xmit   = hdlcdev_xmit;
17318c2ecf20Sopenharmony_ci
17328c2ecf20Sopenharmony_ci	/* register objects with HDLC layer */
17338c2ecf20Sopenharmony_ci	rc = register_hdlc_device(dev);
17348c2ecf20Sopenharmony_ci	if (rc) {
17358c2ecf20Sopenharmony_ci		printk(KERN_WARNING "%s:unable to register hdlc device\n",__FILE__);
17368c2ecf20Sopenharmony_ci		free_netdev(dev);
17378c2ecf20Sopenharmony_ci		return rc;
17388c2ecf20Sopenharmony_ci	}
17398c2ecf20Sopenharmony_ci
17408c2ecf20Sopenharmony_ci	info->netdev = dev;
17418c2ecf20Sopenharmony_ci	return 0;
17428c2ecf20Sopenharmony_ci}
17438c2ecf20Sopenharmony_ci
17448c2ecf20Sopenharmony_ci/**
17458c2ecf20Sopenharmony_ci * hdlcdev_exit - called by device driver when removing device instance
17468c2ecf20Sopenharmony_ci * @info: pointer to device instance information
17478c2ecf20Sopenharmony_ci *
17488c2ecf20Sopenharmony_ci * Do generic HDLC cleanup.
17498c2ecf20Sopenharmony_ci */
17508c2ecf20Sopenharmony_cistatic void hdlcdev_exit(struct slgt_info *info)
17518c2ecf20Sopenharmony_ci{
17528c2ecf20Sopenharmony_ci	if (!info->netdev)
17538c2ecf20Sopenharmony_ci		return;
17548c2ecf20Sopenharmony_ci	unregister_hdlc_device(info->netdev);
17558c2ecf20Sopenharmony_ci	free_netdev(info->netdev);
17568c2ecf20Sopenharmony_ci	info->netdev = NULL;
17578c2ecf20Sopenharmony_ci}
17588c2ecf20Sopenharmony_ci
17598c2ecf20Sopenharmony_ci#endif /* ifdef CONFIG_HDLC */
17608c2ecf20Sopenharmony_ci
17618c2ecf20Sopenharmony_ci/*
17628c2ecf20Sopenharmony_ci * get async data from rx DMA buffers
17638c2ecf20Sopenharmony_ci */
17648c2ecf20Sopenharmony_cistatic void rx_async(struct slgt_info *info)
17658c2ecf20Sopenharmony_ci{
17668c2ecf20Sopenharmony_ci 	struct mgsl_icount *icount = &info->icount;
17678c2ecf20Sopenharmony_ci	unsigned int start, end;
17688c2ecf20Sopenharmony_ci	unsigned char *p;
17698c2ecf20Sopenharmony_ci	unsigned char status;
17708c2ecf20Sopenharmony_ci	struct slgt_desc *bufs = info->rbufs;
17718c2ecf20Sopenharmony_ci	int i, count;
17728c2ecf20Sopenharmony_ci	int chars = 0;
17738c2ecf20Sopenharmony_ci	int stat;
17748c2ecf20Sopenharmony_ci	unsigned char ch;
17758c2ecf20Sopenharmony_ci
17768c2ecf20Sopenharmony_ci	start = end = info->rbuf_current;
17778c2ecf20Sopenharmony_ci
17788c2ecf20Sopenharmony_ci	while(desc_complete(bufs[end])) {
17798c2ecf20Sopenharmony_ci		count = desc_count(bufs[end]) - info->rbuf_index;
17808c2ecf20Sopenharmony_ci		p     = bufs[end].buf + info->rbuf_index;
17818c2ecf20Sopenharmony_ci
17828c2ecf20Sopenharmony_ci		DBGISR(("%s rx_async count=%d\n", info->device_name, count));
17838c2ecf20Sopenharmony_ci		DBGDATA(info, p, count, "rx");
17848c2ecf20Sopenharmony_ci
17858c2ecf20Sopenharmony_ci		for(i=0 ; i < count; i+=2, p+=2) {
17868c2ecf20Sopenharmony_ci			ch = *p;
17878c2ecf20Sopenharmony_ci			icount->rx++;
17888c2ecf20Sopenharmony_ci
17898c2ecf20Sopenharmony_ci			stat = 0;
17908c2ecf20Sopenharmony_ci
17918c2ecf20Sopenharmony_ci			status = *(p + 1) & (BIT1 + BIT0);
17928c2ecf20Sopenharmony_ci			if (status) {
17938c2ecf20Sopenharmony_ci				if (status & BIT1)
17948c2ecf20Sopenharmony_ci					icount->parity++;
17958c2ecf20Sopenharmony_ci				else if (status & BIT0)
17968c2ecf20Sopenharmony_ci					icount->frame++;
17978c2ecf20Sopenharmony_ci				/* discard char if tty control flags say so */
17988c2ecf20Sopenharmony_ci				if (status & info->ignore_status_mask)
17998c2ecf20Sopenharmony_ci					continue;
18008c2ecf20Sopenharmony_ci				if (status & BIT1)
18018c2ecf20Sopenharmony_ci					stat = TTY_PARITY;
18028c2ecf20Sopenharmony_ci				else if (status & BIT0)
18038c2ecf20Sopenharmony_ci					stat = TTY_FRAME;
18048c2ecf20Sopenharmony_ci			}
18058c2ecf20Sopenharmony_ci			tty_insert_flip_char(&info->port, ch, stat);
18068c2ecf20Sopenharmony_ci			chars++;
18078c2ecf20Sopenharmony_ci		}
18088c2ecf20Sopenharmony_ci
18098c2ecf20Sopenharmony_ci		if (i < count) {
18108c2ecf20Sopenharmony_ci			/* receive buffer not completed */
18118c2ecf20Sopenharmony_ci			info->rbuf_index += i;
18128c2ecf20Sopenharmony_ci			mod_timer(&info->rx_timer, jiffies + 1);
18138c2ecf20Sopenharmony_ci			break;
18148c2ecf20Sopenharmony_ci		}
18158c2ecf20Sopenharmony_ci
18168c2ecf20Sopenharmony_ci		info->rbuf_index = 0;
18178c2ecf20Sopenharmony_ci		free_rbufs(info, end, end);
18188c2ecf20Sopenharmony_ci
18198c2ecf20Sopenharmony_ci		if (++end == info->rbuf_count)
18208c2ecf20Sopenharmony_ci			end = 0;
18218c2ecf20Sopenharmony_ci
18228c2ecf20Sopenharmony_ci		/* if entire list searched then no frame available */
18238c2ecf20Sopenharmony_ci		if (end == start)
18248c2ecf20Sopenharmony_ci			break;
18258c2ecf20Sopenharmony_ci	}
18268c2ecf20Sopenharmony_ci
18278c2ecf20Sopenharmony_ci	if (chars)
18288c2ecf20Sopenharmony_ci		tty_flip_buffer_push(&info->port);
18298c2ecf20Sopenharmony_ci}
18308c2ecf20Sopenharmony_ci
18318c2ecf20Sopenharmony_ci/*
18328c2ecf20Sopenharmony_ci * return next bottom half action to perform
18338c2ecf20Sopenharmony_ci */
18348c2ecf20Sopenharmony_cistatic int bh_action(struct slgt_info *info)
18358c2ecf20Sopenharmony_ci{
18368c2ecf20Sopenharmony_ci	unsigned long flags;
18378c2ecf20Sopenharmony_ci	int rc;
18388c2ecf20Sopenharmony_ci
18398c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
18408c2ecf20Sopenharmony_ci
18418c2ecf20Sopenharmony_ci	if (info->pending_bh & BH_RECEIVE) {
18428c2ecf20Sopenharmony_ci		info->pending_bh &= ~BH_RECEIVE;
18438c2ecf20Sopenharmony_ci		rc = BH_RECEIVE;
18448c2ecf20Sopenharmony_ci	} else if (info->pending_bh & BH_TRANSMIT) {
18458c2ecf20Sopenharmony_ci		info->pending_bh &= ~BH_TRANSMIT;
18468c2ecf20Sopenharmony_ci		rc = BH_TRANSMIT;
18478c2ecf20Sopenharmony_ci	} else if (info->pending_bh & BH_STATUS) {
18488c2ecf20Sopenharmony_ci		info->pending_bh &= ~BH_STATUS;
18498c2ecf20Sopenharmony_ci		rc = BH_STATUS;
18508c2ecf20Sopenharmony_ci	} else {
18518c2ecf20Sopenharmony_ci		/* Mark BH routine as complete */
18528c2ecf20Sopenharmony_ci		info->bh_running = false;
18538c2ecf20Sopenharmony_ci		info->bh_requested = false;
18548c2ecf20Sopenharmony_ci		rc = 0;
18558c2ecf20Sopenharmony_ci	}
18568c2ecf20Sopenharmony_ci
18578c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
18588c2ecf20Sopenharmony_ci
18598c2ecf20Sopenharmony_ci	return rc;
18608c2ecf20Sopenharmony_ci}
18618c2ecf20Sopenharmony_ci
18628c2ecf20Sopenharmony_ci/*
18638c2ecf20Sopenharmony_ci * perform bottom half processing
18648c2ecf20Sopenharmony_ci */
18658c2ecf20Sopenharmony_cistatic void bh_handler(struct work_struct *work)
18668c2ecf20Sopenharmony_ci{
18678c2ecf20Sopenharmony_ci	struct slgt_info *info = container_of(work, struct slgt_info, task);
18688c2ecf20Sopenharmony_ci	int action;
18698c2ecf20Sopenharmony_ci
18708c2ecf20Sopenharmony_ci	info->bh_running = true;
18718c2ecf20Sopenharmony_ci
18728c2ecf20Sopenharmony_ci	while((action = bh_action(info))) {
18738c2ecf20Sopenharmony_ci		switch (action) {
18748c2ecf20Sopenharmony_ci		case BH_RECEIVE:
18758c2ecf20Sopenharmony_ci			DBGBH(("%s bh receive\n", info->device_name));
18768c2ecf20Sopenharmony_ci			switch(info->params.mode) {
18778c2ecf20Sopenharmony_ci			case MGSL_MODE_ASYNC:
18788c2ecf20Sopenharmony_ci				rx_async(info);
18798c2ecf20Sopenharmony_ci				break;
18808c2ecf20Sopenharmony_ci			case MGSL_MODE_HDLC:
18818c2ecf20Sopenharmony_ci				while(rx_get_frame(info));
18828c2ecf20Sopenharmony_ci				break;
18838c2ecf20Sopenharmony_ci			case MGSL_MODE_RAW:
18848c2ecf20Sopenharmony_ci			case MGSL_MODE_MONOSYNC:
18858c2ecf20Sopenharmony_ci			case MGSL_MODE_BISYNC:
18868c2ecf20Sopenharmony_ci			case MGSL_MODE_XSYNC:
18878c2ecf20Sopenharmony_ci				while(rx_get_buf(info));
18888c2ecf20Sopenharmony_ci				break;
18898c2ecf20Sopenharmony_ci			}
18908c2ecf20Sopenharmony_ci			/* restart receiver if rx DMA buffers exhausted */
18918c2ecf20Sopenharmony_ci			if (info->rx_restart)
18928c2ecf20Sopenharmony_ci				rx_start(info);
18938c2ecf20Sopenharmony_ci			break;
18948c2ecf20Sopenharmony_ci		case BH_TRANSMIT:
18958c2ecf20Sopenharmony_ci			bh_transmit(info);
18968c2ecf20Sopenharmony_ci			break;
18978c2ecf20Sopenharmony_ci		case BH_STATUS:
18988c2ecf20Sopenharmony_ci			DBGBH(("%s bh status\n", info->device_name));
18998c2ecf20Sopenharmony_ci			info->ri_chkcount = 0;
19008c2ecf20Sopenharmony_ci			info->dsr_chkcount = 0;
19018c2ecf20Sopenharmony_ci			info->dcd_chkcount = 0;
19028c2ecf20Sopenharmony_ci			info->cts_chkcount = 0;
19038c2ecf20Sopenharmony_ci			break;
19048c2ecf20Sopenharmony_ci		default:
19058c2ecf20Sopenharmony_ci			DBGBH(("%s unknown action\n", info->device_name));
19068c2ecf20Sopenharmony_ci			break;
19078c2ecf20Sopenharmony_ci		}
19088c2ecf20Sopenharmony_ci	}
19098c2ecf20Sopenharmony_ci	DBGBH(("%s bh_handler exit\n", info->device_name));
19108c2ecf20Sopenharmony_ci}
19118c2ecf20Sopenharmony_ci
19128c2ecf20Sopenharmony_cistatic void bh_transmit(struct slgt_info *info)
19138c2ecf20Sopenharmony_ci{
19148c2ecf20Sopenharmony_ci	struct tty_struct *tty = info->port.tty;
19158c2ecf20Sopenharmony_ci
19168c2ecf20Sopenharmony_ci	DBGBH(("%s bh_transmit\n", info->device_name));
19178c2ecf20Sopenharmony_ci	if (tty)
19188c2ecf20Sopenharmony_ci		tty_wakeup(tty);
19198c2ecf20Sopenharmony_ci}
19208c2ecf20Sopenharmony_ci
19218c2ecf20Sopenharmony_cistatic void dsr_change(struct slgt_info *info, unsigned short status)
19228c2ecf20Sopenharmony_ci{
19238c2ecf20Sopenharmony_ci	if (status & BIT3) {
19248c2ecf20Sopenharmony_ci		info->signals |= SerialSignal_DSR;
19258c2ecf20Sopenharmony_ci		info->input_signal_events.dsr_up++;
19268c2ecf20Sopenharmony_ci	} else {
19278c2ecf20Sopenharmony_ci		info->signals &= ~SerialSignal_DSR;
19288c2ecf20Sopenharmony_ci		info->input_signal_events.dsr_down++;
19298c2ecf20Sopenharmony_ci	}
19308c2ecf20Sopenharmony_ci	DBGISR(("dsr_change %s signals=%04X\n", info->device_name, info->signals));
19318c2ecf20Sopenharmony_ci	if ((info->dsr_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) {
19328c2ecf20Sopenharmony_ci		slgt_irq_off(info, IRQ_DSR);
19338c2ecf20Sopenharmony_ci		return;
19348c2ecf20Sopenharmony_ci	}
19358c2ecf20Sopenharmony_ci	info->icount.dsr++;
19368c2ecf20Sopenharmony_ci	wake_up_interruptible(&info->status_event_wait_q);
19378c2ecf20Sopenharmony_ci	wake_up_interruptible(&info->event_wait_q);
19388c2ecf20Sopenharmony_ci	info->pending_bh |= BH_STATUS;
19398c2ecf20Sopenharmony_ci}
19408c2ecf20Sopenharmony_ci
19418c2ecf20Sopenharmony_cistatic void cts_change(struct slgt_info *info, unsigned short status)
19428c2ecf20Sopenharmony_ci{
19438c2ecf20Sopenharmony_ci	if (status & BIT2) {
19448c2ecf20Sopenharmony_ci		info->signals |= SerialSignal_CTS;
19458c2ecf20Sopenharmony_ci		info->input_signal_events.cts_up++;
19468c2ecf20Sopenharmony_ci	} else {
19478c2ecf20Sopenharmony_ci		info->signals &= ~SerialSignal_CTS;
19488c2ecf20Sopenharmony_ci		info->input_signal_events.cts_down++;
19498c2ecf20Sopenharmony_ci	}
19508c2ecf20Sopenharmony_ci	DBGISR(("cts_change %s signals=%04X\n", info->device_name, info->signals));
19518c2ecf20Sopenharmony_ci	if ((info->cts_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) {
19528c2ecf20Sopenharmony_ci		slgt_irq_off(info, IRQ_CTS);
19538c2ecf20Sopenharmony_ci		return;
19548c2ecf20Sopenharmony_ci	}
19558c2ecf20Sopenharmony_ci	info->icount.cts++;
19568c2ecf20Sopenharmony_ci	wake_up_interruptible(&info->status_event_wait_q);
19578c2ecf20Sopenharmony_ci	wake_up_interruptible(&info->event_wait_q);
19588c2ecf20Sopenharmony_ci	info->pending_bh |= BH_STATUS;
19598c2ecf20Sopenharmony_ci
19608c2ecf20Sopenharmony_ci	if (tty_port_cts_enabled(&info->port)) {
19618c2ecf20Sopenharmony_ci		if (info->port.tty) {
19628c2ecf20Sopenharmony_ci			if (info->port.tty->hw_stopped) {
19638c2ecf20Sopenharmony_ci				if (info->signals & SerialSignal_CTS) {
19648c2ecf20Sopenharmony_ci		 			info->port.tty->hw_stopped = 0;
19658c2ecf20Sopenharmony_ci					info->pending_bh |= BH_TRANSMIT;
19668c2ecf20Sopenharmony_ci					return;
19678c2ecf20Sopenharmony_ci				}
19688c2ecf20Sopenharmony_ci			} else {
19698c2ecf20Sopenharmony_ci				if (!(info->signals & SerialSignal_CTS))
19708c2ecf20Sopenharmony_ci		 			info->port.tty->hw_stopped = 1;
19718c2ecf20Sopenharmony_ci			}
19728c2ecf20Sopenharmony_ci		}
19738c2ecf20Sopenharmony_ci	}
19748c2ecf20Sopenharmony_ci}
19758c2ecf20Sopenharmony_ci
19768c2ecf20Sopenharmony_cistatic void dcd_change(struct slgt_info *info, unsigned short status)
19778c2ecf20Sopenharmony_ci{
19788c2ecf20Sopenharmony_ci	if (status & BIT1) {
19798c2ecf20Sopenharmony_ci		info->signals |= SerialSignal_DCD;
19808c2ecf20Sopenharmony_ci		info->input_signal_events.dcd_up++;
19818c2ecf20Sopenharmony_ci	} else {
19828c2ecf20Sopenharmony_ci		info->signals &= ~SerialSignal_DCD;
19838c2ecf20Sopenharmony_ci		info->input_signal_events.dcd_down++;
19848c2ecf20Sopenharmony_ci	}
19858c2ecf20Sopenharmony_ci	DBGISR(("dcd_change %s signals=%04X\n", info->device_name, info->signals));
19868c2ecf20Sopenharmony_ci	if ((info->dcd_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) {
19878c2ecf20Sopenharmony_ci		slgt_irq_off(info, IRQ_DCD);
19888c2ecf20Sopenharmony_ci		return;
19898c2ecf20Sopenharmony_ci	}
19908c2ecf20Sopenharmony_ci	info->icount.dcd++;
19918c2ecf20Sopenharmony_ci#if SYNCLINK_GENERIC_HDLC
19928c2ecf20Sopenharmony_ci	if (info->netcount) {
19938c2ecf20Sopenharmony_ci		if (info->signals & SerialSignal_DCD)
19948c2ecf20Sopenharmony_ci			netif_carrier_on(info->netdev);
19958c2ecf20Sopenharmony_ci		else
19968c2ecf20Sopenharmony_ci			netif_carrier_off(info->netdev);
19978c2ecf20Sopenharmony_ci	}
19988c2ecf20Sopenharmony_ci#endif
19998c2ecf20Sopenharmony_ci	wake_up_interruptible(&info->status_event_wait_q);
20008c2ecf20Sopenharmony_ci	wake_up_interruptible(&info->event_wait_q);
20018c2ecf20Sopenharmony_ci	info->pending_bh |= BH_STATUS;
20028c2ecf20Sopenharmony_ci
20038c2ecf20Sopenharmony_ci	if (tty_port_check_carrier(&info->port)) {
20048c2ecf20Sopenharmony_ci		if (info->signals & SerialSignal_DCD)
20058c2ecf20Sopenharmony_ci			wake_up_interruptible(&info->port.open_wait);
20068c2ecf20Sopenharmony_ci		else {
20078c2ecf20Sopenharmony_ci			if (info->port.tty)
20088c2ecf20Sopenharmony_ci				tty_hangup(info->port.tty);
20098c2ecf20Sopenharmony_ci		}
20108c2ecf20Sopenharmony_ci	}
20118c2ecf20Sopenharmony_ci}
20128c2ecf20Sopenharmony_ci
20138c2ecf20Sopenharmony_cistatic void ri_change(struct slgt_info *info, unsigned short status)
20148c2ecf20Sopenharmony_ci{
20158c2ecf20Sopenharmony_ci	if (status & BIT0) {
20168c2ecf20Sopenharmony_ci		info->signals |= SerialSignal_RI;
20178c2ecf20Sopenharmony_ci		info->input_signal_events.ri_up++;
20188c2ecf20Sopenharmony_ci	} else {
20198c2ecf20Sopenharmony_ci		info->signals &= ~SerialSignal_RI;
20208c2ecf20Sopenharmony_ci		info->input_signal_events.ri_down++;
20218c2ecf20Sopenharmony_ci	}
20228c2ecf20Sopenharmony_ci	DBGISR(("ri_change %s signals=%04X\n", info->device_name, info->signals));
20238c2ecf20Sopenharmony_ci	if ((info->ri_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) {
20248c2ecf20Sopenharmony_ci		slgt_irq_off(info, IRQ_RI);
20258c2ecf20Sopenharmony_ci		return;
20268c2ecf20Sopenharmony_ci	}
20278c2ecf20Sopenharmony_ci	info->icount.rng++;
20288c2ecf20Sopenharmony_ci	wake_up_interruptible(&info->status_event_wait_q);
20298c2ecf20Sopenharmony_ci	wake_up_interruptible(&info->event_wait_q);
20308c2ecf20Sopenharmony_ci	info->pending_bh |= BH_STATUS;
20318c2ecf20Sopenharmony_ci}
20328c2ecf20Sopenharmony_ci
20338c2ecf20Sopenharmony_cistatic void isr_rxdata(struct slgt_info *info)
20348c2ecf20Sopenharmony_ci{
20358c2ecf20Sopenharmony_ci	unsigned int count = info->rbuf_fill_count;
20368c2ecf20Sopenharmony_ci	unsigned int i = info->rbuf_fill_index;
20378c2ecf20Sopenharmony_ci	unsigned short reg;
20388c2ecf20Sopenharmony_ci
20398c2ecf20Sopenharmony_ci	while (rd_reg16(info, SSR) & IRQ_RXDATA) {
20408c2ecf20Sopenharmony_ci		reg = rd_reg16(info, RDR);
20418c2ecf20Sopenharmony_ci		DBGISR(("isr_rxdata %s RDR=%04X\n", info->device_name, reg));
20428c2ecf20Sopenharmony_ci		if (desc_complete(info->rbufs[i])) {
20438c2ecf20Sopenharmony_ci			/* all buffers full */
20448c2ecf20Sopenharmony_ci			rx_stop(info);
20458c2ecf20Sopenharmony_ci			info->rx_restart = true;
20468c2ecf20Sopenharmony_ci			continue;
20478c2ecf20Sopenharmony_ci		}
20488c2ecf20Sopenharmony_ci		info->rbufs[i].buf[count++] = (unsigned char)reg;
20498c2ecf20Sopenharmony_ci		/* async mode saves status byte to buffer for each data byte */
20508c2ecf20Sopenharmony_ci		if (info->params.mode == MGSL_MODE_ASYNC)
20518c2ecf20Sopenharmony_ci			info->rbufs[i].buf[count++] = (unsigned char)(reg >> 8);
20528c2ecf20Sopenharmony_ci		if (count == info->rbuf_fill_level || (reg & BIT10)) {
20538c2ecf20Sopenharmony_ci			/* buffer full or end of frame */
20548c2ecf20Sopenharmony_ci			set_desc_count(info->rbufs[i], count);
20558c2ecf20Sopenharmony_ci			set_desc_status(info->rbufs[i], BIT15 | (reg >> 8));
20568c2ecf20Sopenharmony_ci			info->rbuf_fill_count = count = 0;
20578c2ecf20Sopenharmony_ci			if (++i == info->rbuf_count)
20588c2ecf20Sopenharmony_ci				i = 0;
20598c2ecf20Sopenharmony_ci			info->pending_bh |= BH_RECEIVE;
20608c2ecf20Sopenharmony_ci		}
20618c2ecf20Sopenharmony_ci	}
20628c2ecf20Sopenharmony_ci
20638c2ecf20Sopenharmony_ci	info->rbuf_fill_index = i;
20648c2ecf20Sopenharmony_ci	info->rbuf_fill_count = count;
20658c2ecf20Sopenharmony_ci}
20668c2ecf20Sopenharmony_ci
20678c2ecf20Sopenharmony_cistatic void isr_serial(struct slgt_info *info)
20688c2ecf20Sopenharmony_ci{
20698c2ecf20Sopenharmony_ci	unsigned short status = rd_reg16(info, SSR);
20708c2ecf20Sopenharmony_ci
20718c2ecf20Sopenharmony_ci	DBGISR(("%s isr_serial status=%04X\n", info->device_name, status));
20728c2ecf20Sopenharmony_ci
20738c2ecf20Sopenharmony_ci	wr_reg16(info, SSR, status); /* clear pending */
20748c2ecf20Sopenharmony_ci
20758c2ecf20Sopenharmony_ci	info->irq_occurred = true;
20768c2ecf20Sopenharmony_ci
20778c2ecf20Sopenharmony_ci	if (info->params.mode == MGSL_MODE_ASYNC) {
20788c2ecf20Sopenharmony_ci		if (status & IRQ_TXIDLE) {
20798c2ecf20Sopenharmony_ci			if (info->tx_active)
20808c2ecf20Sopenharmony_ci				isr_txeom(info, status);
20818c2ecf20Sopenharmony_ci		}
20828c2ecf20Sopenharmony_ci		if (info->rx_pio && (status & IRQ_RXDATA))
20838c2ecf20Sopenharmony_ci			isr_rxdata(info);
20848c2ecf20Sopenharmony_ci		if ((status & IRQ_RXBREAK) && (status & RXBREAK)) {
20858c2ecf20Sopenharmony_ci			info->icount.brk++;
20868c2ecf20Sopenharmony_ci			/* process break detection if tty control allows */
20878c2ecf20Sopenharmony_ci			if (info->port.tty) {
20888c2ecf20Sopenharmony_ci				if (!(status & info->ignore_status_mask)) {
20898c2ecf20Sopenharmony_ci					if (info->read_status_mask & MASK_BREAK) {
20908c2ecf20Sopenharmony_ci						tty_insert_flip_char(&info->port, 0, TTY_BREAK);
20918c2ecf20Sopenharmony_ci						if (info->port.flags & ASYNC_SAK)
20928c2ecf20Sopenharmony_ci							do_SAK(info->port.tty);
20938c2ecf20Sopenharmony_ci					}
20948c2ecf20Sopenharmony_ci				}
20958c2ecf20Sopenharmony_ci			}
20968c2ecf20Sopenharmony_ci		}
20978c2ecf20Sopenharmony_ci	} else {
20988c2ecf20Sopenharmony_ci		if (status & (IRQ_TXIDLE + IRQ_TXUNDER))
20998c2ecf20Sopenharmony_ci			isr_txeom(info, status);
21008c2ecf20Sopenharmony_ci		if (info->rx_pio && (status & IRQ_RXDATA))
21018c2ecf20Sopenharmony_ci			isr_rxdata(info);
21028c2ecf20Sopenharmony_ci		if (status & IRQ_RXIDLE) {
21038c2ecf20Sopenharmony_ci			if (status & RXIDLE)
21048c2ecf20Sopenharmony_ci				info->icount.rxidle++;
21058c2ecf20Sopenharmony_ci			else
21068c2ecf20Sopenharmony_ci				info->icount.exithunt++;
21078c2ecf20Sopenharmony_ci			wake_up_interruptible(&info->event_wait_q);
21088c2ecf20Sopenharmony_ci		}
21098c2ecf20Sopenharmony_ci
21108c2ecf20Sopenharmony_ci		if (status & IRQ_RXOVER)
21118c2ecf20Sopenharmony_ci			rx_start(info);
21128c2ecf20Sopenharmony_ci	}
21138c2ecf20Sopenharmony_ci
21148c2ecf20Sopenharmony_ci	if (status & IRQ_DSR)
21158c2ecf20Sopenharmony_ci		dsr_change(info, status);
21168c2ecf20Sopenharmony_ci	if (status & IRQ_CTS)
21178c2ecf20Sopenharmony_ci		cts_change(info, status);
21188c2ecf20Sopenharmony_ci	if (status & IRQ_DCD)
21198c2ecf20Sopenharmony_ci		dcd_change(info, status);
21208c2ecf20Sopenharmony_ci	if (status & IRQ_RI)
21218c2ecf20Sopenharmony_ci		ri_change(info, status);
21228c2ecf20Sopenharmony_ci}
21238c2ecf20Sopenharmony_ci
21248c2ecf20Sopenharmony_cistatic void isr_rdma(struct slgt_info *info)
21258c2ecf20Sopenharmony_ci{
21268c2ecf20Sopenharmony_ci	unsigned int status = rd_reg32(info, RDCSR);
21278c2ecf20Sopenharmony_ci
21288c2ecf20Sopenharmony_ci	DBGISR(("%s isr_rdma status=%08x\n", info->device_name, status));
21298c2ecf20Sopenharmony_ci
21308c2ecf20Sopenharmony_ci	/* RDCSR (rx DMA control/status)
21318c2ecf20Sopenharmony_ci	 *
21328c2ecf20Sopenharmony_ci	 * 31..07  reserved
21338c2ecf20Sopenharmony_ci	 * 06      save status byte to DMA buffer
21348c2ecf20Sopenharmony_ci	 * 05      error
21358c2ecf20Sopenharmony_ci	 * 04      eol (end of list)
21368c2ecf20Sopenharmony_ci	 * 03      eob (end of buffer)
21378c2ecf20Sopenharmony_ci	 * 02      IRQ enable
21388c2ecf20Sopenharmony_ci	 * 01      reset
21398c2ecf20Sopenharmony_ci	 * 00      enable
21408c2ecf20Sopenharmony_ci	 */
21418c2ecf20Sopenharmony_ci	wr_reg32(info, RDCSR, status);	/* clear pending */
21428c2ecf20Sopenharmony_ci
21438c2ecf20Sopenharmony_ci	if (status & (BIT5 + BIT4)) {
21448c2ecf20Sopenharmony_ci		DBGISR(("%s isr_rdma rx_restart=1\n", info->device_name));
21458c2ecf20Sopenharmony_ci		info->rx_restart = true;
21468c2ecf20Sopenharmony_ci	}
21478c2ecf20Sopenharmony_ci	info->pending_bh |= BH_RECEIVE;
21488c2ecf20Sopenharmony_ci}
21498c2ecf20Sopenharmony_ci
21508c2ecf20Sopenharmony_cistatic void isr_tdma(struct slgt_info *info)
21518c2ecf20Sopenharmony_ci{
21528c2ecf20Sopenharmony_ci	unsigned int status = rd_reg32(info, TDCSR);
21538c2ecf20Sopenharmony_ci
21548c2ecf20Sopenharmony_ci	DBGISR(("%s isr_tdma status=%08x\n", info->device_name, status));
21558c2ecf20Sopenharmony_ci
21568c2ecf20Sopenharmony_ci	/* TDCSR (tx DMA control/status)
21578c2ecf20Sopenharmony_ci	 *
21588c2ecf20Sopenharmony_ci	 * 31..06  reserved
21598c2ecf20Sopenharmony_ci	 * 05      error
21608c2ecf20Sopenharmony_ci	 * 04      eol (end of list)
21618c2ecf20Sopenharmony_ci	 * 03      eob (end of buffer)
21628c2ecf20Sopenharmony_ci	 * 02      IRQ enable
21638c2ecf20Sopenharmony_ci	 * 01      reset
21648c2ecf20Sopenharmony_ci	 * 00      enable
21658c2ecf20Sopenharmony_ci	 */
21668c2ecf20Sopenharmony_ci	wr_reg32(info, TDCSR, status);	/* clear pending */
21678c2ecf20Sopenharmony_ci
21688c2ecf20Sopenharmony_ci	if (status & (BIT5 + BIT4 + BIT3)) {
21698c2ecf20Sopenharmony_ci		// another transmit buffer has completed
21708c2ecf20Sopenharmony_ci		// run bottom half to get more send data from user
21718c2ecf20Sopenharmony_ci		info->pending_bh |= BH_TRANSMIT;
21728c2ecf20Sopenharmony_ci	}
21738c2ecf20Sopenharmony_ci}
21748c2ecf20Sopenharmony_ci
21758c2ecf20Sopenharmony_ci/*
21768c2ecf20Sopenharmony_ci * return true if there are unsent tx DMA buffers, otherwise false
21778c2ecf20Sopenharmony_ci *
21788c2ecf20Sopenharmony_ci * if there are unsent buffers then info->tbuf_start
21798c2ecf20Sopenharmony_ci * is set to index of first unsent buffer
21808c2ecf20Sopenharmony_ci */
21818c2ecf20Sopenharmony_cistatic bool unsent_tbufs(struct slgt_info *info)
21828c2ecf20Sopenharmony_ci{
21838c2ecf20Sopenharmony_ci	unsigned int i = info->tbuf_current;
21848c2ecf20Sopenharmony_ci	bool rc = false;
21858c2ecf20Sopenharmony_ci
21868c2ecf20Sopenharmony_ci	/*
21878c2ecf20Sopenharmony_ci	 * search backwards from last loaded buffer (precedes tbuf_current)
21888c2ecf20Sopenharmony_ci	 * for first unsent buffer (desc_count > 0)
21898c2ecf20Sopenharmony_ci	 */
21908c2ecf20Sopenharmony_ci
21918c2ecf20Sopenharmony_ci	do {
21928c2ecf20Sopenharmony_ci		if (i)
21938c2ecf20Sopenharmony_ci			i--;
21948c2ecf20Sopenharmony_ci		else
21958c2ecf20Sopenharmony_ci			i = info->tbuf_count - 1;
21968c2ecf20Sopenharmony_ci		if (!desc_count(info->tbufs[i]))
21978c2ecf20Sopenharmony_ci			break;
21988c2ecf20Sopenharmony_ci		info->tbuf_start = i;
21998c2ecf20Sopenharmony_ci		rc = true;
22008c2ecf20Sopenharmony_ci	} while (i != info->tbuf_current);
22018c2ecf20Sopenharmony_ci
22028c2ecf20Sopenharmony_ci	return rc;
22038c2ecf20Sopenharmony_ci}
22048c2ecf20Sopenharmony_ci
22058c2ecf20Sopenharmony_cistatic void isr_txeom(struct slgt_info *info, unsigned short status)
22068c2ecf20Sopenharmony_ci{
22078c2ecf20Sopenharmony_ci	DBGISR(("%s txeom status=%04x\n", info->device_name, status));
22088c2ecf20Sopenharmony_ci
22098c2ecf20Sopenharmony_ci	slgt_irq_off(info, IRQ_TXDATA + IRQ_TXIDLE + IRQ_TXUNDER);
22108c2ecf20Sopenharmony_ci	tdma_reset(info);
22118c2ecf20Sopenharmony_ci	if (status & IRQ_TXUNDER) {
22128c2ecf20Sopenharmony_ci		unsigned short val = rd_reg16(info, TCR);
22138c2ecf20Sopenharmony_ci		wr_reg16(info, TCR, (unsigned short)(val | BIT2)); /* set reset bit */
22148c2ecf20Sopenharmony_ci		wr_reg16(info, TCR, val); /* clear reset bit */
22158c2ecf20Sopenharmony_ci	}
22168c2ecf20Sopenharmony_ci
22178c2ecf20Sopenharmony_ci	if (info->tx_active) {
22188c2ecf20Sopenharmony_ci		if (info->params.mode != MGSL_MODE_ASYNC) {
22198c2ecf20Sopenharmony_ci			if (status & IRQ_TXUNDER)
22208c2ecf20Sopenharmony_ci				info->icount.txunder++;
22218c2ecf20Sopenharmony_ci			else if (status & IRQ_TXIDLE)
22228c2ecf20Sopenharmony_ci				info->icount.txok++;
22238c2ecf20Sopenharmony_ci		}
22248c2ecf20Sopenharmony_ci
22258c2ecf20Sopenharmony_ci		if (unsent_tbufs(info)) {
22268c2ecf20Sopenharmony_ci			tx_start(info);
22278c2ecf20Sopenharmony_ci			update_tx_timer(info);
22288c2ecf20Sopenharmony_ci			return;
22298c2ecf20Sopenharmony_ci		}
22308c2ecf20Sopenharmony_ci		info->tx_active = false;
22318c2ecf20Sopenharmony_ci
22328c2ecf20Sopenharmony_ci		del_timer(&info->tx_timer);
22338c2ecf20Sopenharmony_ci
22348c2ecf20Sopenharmony_ci		if (info->params.mode != MGSL_MODE_ASYNC && info->drop_rts_on_tx_done) {
22358c2ecf20Sopenharmony_ci			info->signals &= ~SerialSignal_RTS;
22368c2ecf20Sopenharmony_ci			info->drop_rts_on_tx_done = false;
22378c2ecf20Sopenharmony_ci			set_gtsignals(info);
22388c2ecf20Sopenharmony_ci		}
22398c2ecf20Sopenharmony_ci
22408c2ecf20Sopenharmony_ci#if SYNCLINK_GENERIC_HDLC
22418c2ecf20Sopenharmony_ci		if (info->netcount)
22428c2ecf20Sopenharmony_ci			hdlcdev_tx_done(info);
22438c2ecf20Sopenharmony_ci		else
22448c2ecf20Sopenharmony_ci#endif
22458c2ecf20Sopenharmony_ci		{
22468c2ecf20Sopenharmony_ci			if (info->port.tty && (info->port.tty->stopped || info->port.tty->hw_stopped)) {
22478c2ecf20Sopenharmony_ci				tx_stop(info);
22488c2ecf20Sopenharmony_ci				return;
22498c2ecf20Sopenharmony_ci			}
22508c2ecf20Sopenharmony_ci			info->pending_bh |= BH_TRANSMIT;
22518c2ecf20Sopenharmony_ci		}
22528c2ecf20Sopenharmony_ci	}
22538c2ecf20Sopenharmony_ci}
22548c2ecf20Sopenharmony_ci
22558c2ecf20Sopenharmony_cistatic void isr_gpio(struct slgt_info *info, unsigned int changed, unsigned int state)
22568c2ecf20Sopenharmony_ci{
22578c2ecf20Sopenharmony_ci	struct cond_wait *w, *prev;
22588c2ecf20Sopenharmony_ci
22598c2ecf20Sopenharmony_ci	/* wake processes waiting for specific transitions */
22608c2ecf20Sopenharmony_ci	for (w = info->gpio_wait_q, prev = NULL ; w != NULL ; w = w->next) {
22618c2ecf20Sopenharmony_ci		if (w->data & changed) {
22628c2ecf20Sopenharmony_ci			w->data = state;
22638c2ecf20Sopenharmony_ci			wake_up_interruptible(&w->q);
22648c2ecf20Sopenharmony_ci			if (prev != NULL)
22658c2ecf20Sopenharmony_ci				prev->next = w->next;
22668c2ecf20Sopenharmony_ci			else
22678c2ecf20Sopenharmony_ci				info->gpio_wait_q = w->next;
22688c2ecf20Sopenharmony_ci		} else
22698c2ecf20Sopenharmony_ci			prev = w;
22708c2ecf20Sopenharmony_ci	}
22718c2ecf20Sopenharmony_ci}
22728c2ecf20Sopenharmony_ci
22738c2ecf20Sopenharmony_ci/* interrupt service routine
22748c2ecf20Sopenharmony_ci *
22758c2ecf20Sopenharmony_ci * 	irq	interrupt number
22768c2ecf20Sopenharmony_ci * 	dev_id	device ID supplied during interrupt registration
22778c2ecf20Sopenharmony_ci */
22788c2ecf20Sopenharmony_cistatic irqreturn_t slgt_interrupt(int dummy, void *dev_id)
22798c2ecf20Sopenharmony_ci{
22808c2ecf20Sopenharmony_ci	struct slgt_info *info = dev_id;
22818c2ecf20Sopenharmony_ci	unsigned int gsr;
22828c2ecf20Sopenharmony_ci	unsigned int i;
22838c2ecf20Sopenharmony_ci
22848c2ecf20Sopenharmony_ci	DBGISR(("slgt_interrupt irq=%d entry\n", info->irq_level));
22858c2ecf20Sopenharmony_ci
22868c2ecf20Sopenharmony_ci	while((gsr = rd_reg32(info, GSR) & 0xffffff00)) {
22878c2ecf20Sopenharmony_ci		DBGISR(("%s gsr=%08x\n", info->device_name, gsr));
22888c2ecf20Sopenharmony_ci		info->irq_occurred = true;
22898c2ecf20Sopenharmony_ci		for(i=0; i < info->port_count ; i++) {
22908c2ecf20Sopenharmony_ci			if (info->port_array[i] == NULL)
22918c2ecf20Sopenharmony_ci				continue;
22928c2ecf20Sopenharmony_ci			spin_lock(&info->port_array[i]->lock);
22938c2ecf20Sopenharmony_ci			if (gsr & (BIT8 << i))
22948c2ecf20Sopenharmony_ci				isr_serial(info->port_array[i]);
22958c2ecf20Sopenharmony_ci			if (gsr & (BIT16 << (i*2)))
22968c2ecf20Sopenharmony_ci				isr_rdma(info->port_array[i]);
22978c2ecf20Sopenharmony_ci			if (gsr & (BIT17 << (i*2)))
22988c2ecf20Sopenharmony_ci				isr_tdma(info->port_array[i]);
22998c2ecf20Sopenharmony_ci			spin_unlock(&info->port_array[i]->lock);
23008c2ecf20Sopenharmony_ci		}
23018c2ecf20Sopenharmony_ci	}
23028c2ecf20Sopenharmony_ci
23038c2ecf20Sopenharmony_ci	if (info->gpio_present) {
23048c2ecf20Sopenharmony_ci		unsigned int state;
23058c2ecf20Sopenharmony_ci		unsigned int changed;
23068c2ecf20Sopenharmony_ci		spin_lock(&info->lock);
23078c2ecf20Sopenharmony_ci		while ((changed = rd_reg32(info, IOSR)) != 0) {
23088c2ecf20Sopenharmony_ci			DBGISR(("%s iosr=%08x\n", info->device_name, changed));
23098c2ecf20Sopenharmony_ci			/* read latched state of GPIO signals */
23108c2ecf20Sopenharmony_ci			state = rd_reg32(info, IOVR);
23118c2ecf20Sopenharmony_ci			/* clear pending GPIO interrupt bits */
23128c2ecf20Sopenharmony_ci			wr_reg32(info, IOSR, changed);
23138c2ecf20Sopenharmony_ci			for (i=0 ; i < info->port_count ; i++) {
23148c2ecf20Sopenharmony_ci				if (info->port_array[i] != NULL)
23158c2ecf20Sopenharmony_ci					isr_gpio(info->port_array[i], changed, state);
23168c2ecf20Sopenharmony_ci			}
23178c2ecf20Sopenharmony_ci		}
23188c2ecf20Sopenharmony_ci		spin_unlock(&info->lock);
23198c2ecf20Sopenharmony_ci	}
23208c2ecf20Sopenharmony_ci
23218c2ecf20Sopenharmony_ci	for(i=0; i < info->port_count ; i++) {
23228c2ecf20Sopenharmony_ci		struct slgt_info *port = info->port_array[i];
23238c2ecf20Sopenharmony_ci		if (port == NULL)
23248c2ecf20Sopenharmony_ci			continue;
23258c2ecf20Sopenharmony_ci		spin_lock(&port->lock);
23268c2ecf20Sopenharmony_ci		if ((port->port.count || port->netcount) &&
23278c2ecf20Sopenharmony_ci		    port->pending_bh && !port->bh_running &&
23288c2ecf20Sopenharmony_ci		    !port->bh_requested) {
23298c2ecf20Sopenharmony_ci			DBGISR(("%s bh queued\n", port->device_name));
23308c2ecf20Sopenharmony_ci			schedule_work(&port->task);
23318c2ecf20Sopenharmony_ci			port->bh_requested = true;
23328c2ecf20Sopenharmony_ci		}
23338c2ecf20Sopenharmony_ci		spin_unlock(&port->lock);
23348c2ecf20Sopenharmony_ci	}
23358c2ecf20Sopenharmony_ci
23368c2ecf20Sopenharmony_ci	DBGISR(("slgt_interrupt irq=%d exit\n", info->irq_level));
23378c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
23388c2ecf20Sopenharmony_ci}
23398c2ecf20Sopenharmony_ci
23408c2ecf20Sopenharmony_cistatic int startup(struct slgt_info *info)
23418c2ecf20Sopenharmony_ci{
23428c2ecf20Sopenharmony_ci	DBGINFO(("%s startup\n", info->device_name));
23438c2ecf20Sopenharmony_ci
23448c2ecf20Sopenharmony_ci	if (tty_port_initialized(&info->port))
23458c2ecf20Sopenharmony_ci		return 0;
23468c2ecf20Sopenharmony_ci
23478c2ecf20Sopenharmony_ci	if (!info->tx_buf) {
23488c2ecf20Sopenharmony_ci		info->tx_buf = kmalloc(info->max_frame_size, GFP_KERNEL);
23498c2ecf20Sopenharmony_ci		if (!info->tx_buf) {
23508c2ecf20Sopenharmony_ci			DBGERR(("%s can't allocate tx buffer\n", info->device_name));
23518c2ecf20Sopenharmony_ci			return -ENOMEM;
23528c2ecf20Sopenharmony_ci		}
23538c2ecf20Sopenharmony_ci	}
23548c2ecf20Sopenharmony_ci
23558c2ecf20Sopenharmony_ci	info->pending_bh = 0;
23568c2ecf20Sopenharmony_ci
23578c2ecf20Sopenharmony_ci	memset(&info->icount, 0, sizeof(info->icount));
23588c2ecf20Sopenharmony_ci
23598c2ecf20Sopenharmony_ci	/* program hardware for current parameters */
23608c2ecf20Sopenharmony_ci	change_params(info);
23618c2ecf20Sopenharmony_ci
23628c2ecf20Sopenharmony_ci	if (info->port.tty)
23638c2ecf20Sopenharmony_ci		clear_bit(TTY_IO_ERROR, &info->port.tty->flags);
23648c2ecf20Sopenharmony_ci
23658c2ecf20Sopenharmony_ci	tty_port_set_initialized(&info->port, 1);
23668c2ecf20Sopenharmony_ci
23678c2ecf20Sopenharmony_ci	return 0;
23688c2ecf20Sopenharmony_ci}
23698c2ecf20Sopenharmony_ci
23708c2ecf20Sopenharmony_ci/*
23718c2ecf20Sopenharmony_ci *  called by close() and hangup() to shutdown hardware
23728c2ecf20Sopenharmony_ci */
23738c2ecf20Sopenharmony_cistatic void shutdown(struct slgt_info *info)
23748c2ecf20Sopenharmony_ci{
23758c2ecf20Sopenharmony_ci	unsigned long flags;
23768c2ecf20Sopenharmony_ci
23778c2ecf20Sopenharmony_ci	if (!tty_port_initialized(&info->port))
23788c2ecf20Sopenharmony_ci		return;
23798c2ecf20Sopenharmony_ci
23808c2ecf20Sopenharmony_ci	DBGINFO(("%s shutdown\n", info->device_name));
23818c2ecf20Sopenharmony_ci
23828c2ecf20Sopenharmony_ci	/* clear status wait queue because status changes */
23838c2ecf20Sopenharmony_ci	/* can't happen after shutting down the hardware */
23848c2ecf20Sopenharmony_ci	wake_up_interruptible(&info->status_event_wait_q);
23858c2ecf20Sopenharmony_ci	wake_up_interruptible(&info->event_wait_q);
23868c2ecf20Sopenharmony_ci
23878c2ecf20Sopenharmony_ci	del_timer_sync(&info->tx_timer);
23888c2ecf20Sopenharmony_ci	del_timer_sync(&info->rx_timer);
23898c2ecf20Sopenharmony_ci
23908c2ecf20Sopenharmony_ci	kfree(info->tx_buf);
23918c2ecf20Sopenharmony_ci	info->tx_buf = NULL;
23928c2ecf20Sopenharmony_ci
23938c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
23948c2ecf20Sopenharmony_ci
23958c2ecf20Sopenharmony_ci	tx_stop(info);
23968c2ecf20Sopenharmony_ci	rx_stop(info);
23978c2ecf20Sopenharmony_ci
23988c2ecf20Sopenharmony_ci	slgt_irq_off(info, IRQ_ALL | IRQ_MASTER);
23998c2ecf20Sopenharmony_ci
24008c2ecf20Sopenharmony_ci 	if (!info->port.tty || info->port.tty->termios.c_cflag & HUPCL) {
24018c2ecf20Sopenharmony_ci		info->signals &= ~(SerialSignal_RTS | SerialSignal_DTR);
24028c2ecf20Sopenharmony_ci		set_gtsignals(info);
24038c2ecf20Sopenharmony_ci	}
24048c2ecf20Sopenharmony_ci
24058c2ecf20Sopenharmony_ci	flush_cond_wait(&info->gpio_wait_q);
24068c2ecf20Sopenharmony_ci
24078c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
24088c2ecf20Sopenharmony_ci
24098c2ecf20Sopenharmony_ci	if (info->port.tty)
24108c2ecf20Sopenharmony_ci		set_bit(TTY_IO_ERROR, &info->port.tty->flags);
24118c2ecf20Sopenharmony_ci
24128c2ecf20Sopenharmony_ci	tty_port_set_initialized(&info->port, 0);
24138c2ecf20Sopenharmony_ci}
24148c2ecf20Sopenharmony_ci
24158c2ecf20Sopenharmony_cistatic void program_hw(struct slgt_info *info)
24168c2ecf20Sopenharmony_ci{
24178c2ecf20Sopenharmony_ci	unsigned long flags;
24188c2ecf20Sopenharmony_ci
24198c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
24208c2ecf20Sopenharmony_ci
24218c2ecf20Sopenharmony_ci	rx_stop(info);
24228c2ecf20Sopenharmony_ci	tx_stop(info);
24238c2ecf20Sopenharmony_ci
24248c2ecf20Sopenharmony_ci	if (info->params.mode != MGSL_MODE_ASYNC ||
24258c2ecf20Sopenharmony_ci	    info->netcount)
24268c2ecf20Sopenharmony_ci		sync_mode(info);
24278c2ecf20Sopenharmony_ci	else
24288c2ecf20Sopenharmony_ci		async_mode(info);
24298c2ecf20Sopenharmony_ci
24308c2ecf20Sopenharmony_ci	set_gtsignals(info);
24318c2ecf20Sopenharmony_ci
24328c2ecf20Sopenharmony_ci	info->dcd_chkcount = 0;
24338c2ecf20Sopenharmony_ci	info->cts_chkcount = 0;
24348c2ecf20Sopenharmony_ci	info->ri_chkcount = 0;
24358c2ecf20Sopenharmony_ci	info->dsr_chkcount = 0;
24368c2ecf20Sopenharmony_ci
24378c2ecf20Sopenharmony_ci	slgt_irq_on(info, IRQ_DCD | IRQ_CTS | IRQ_DSR | IRQ_RI);
24388c2ecf20Sopenharmony_ci	get_gtsignals(info);
24398c2ecf20Sopenharmony_ci
24408c2ecf20Sopenharmony_ci	if (info->netcount ||
24418c2ecf20Sopenharmony_ci	    (info->port.tty && info->port.tty->termios.c_cflag & CREAD))
24428c2ecf20Sopenharmony_ci		rx_start(info);
24438c2ecf20Sopenharmony_ci
24448c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
24458c2ecf20Sopenharmony_ci}
24468c2ecf20Sopenharmony_ci
24478c2ecf20Sopenharmony_ci/*
24488c2ecf20Sopenharmony_ci * reconfigure adapter based on new parameters
24498c2ecf20Sopenharmony_ci */
24508c2ecf20Sopenharmony_cistatic void change_params(struct slgt_info *info)
24518c2ecf20Sopenharmony_ci{
24528c2ecf20Sopenharmony_ci	unsigned cflag;
24538c2ecf20Sopenharmony_ci	int bits_per_char;
24548c2ecf20Sopenharmony_ci
24558c2ecf20Sopenharmony_ci	if (!info->port.tty)
24568c2ecf20Sopenharmony_ci		return;
24578c2ecf20Sopenharmony_ci	DBGINFO(("%s change_params\n", info->device_name));
24588c2ecf20Sopenharmony_ci
24598c2ecf20Sopenharmony_ci	cflag = info->port.tty->termios.c_cflag;
24608c2ecf20Sopenharmony_ci
24618c2ecf20Sopenharmony_ci	/* if B0 rate (hangup) specified then negate RTS and DTR */
24628c2ecf20Sopenharmony_ci	/* otherwise assert RTS and DTR */
24638c2ecf20Sopenharmony_ci 	if (cflag & CBAUD)
24648c2ecf20Sopenharmony_ci		info->signals |= SerialSignal_RTS | SerialSignal_DTR;
24658c2ecf20Sopenharmony_ci	else
24668c2ecf20Sopenharmony_ci		info->signals &= ~(SerialSignal_RTS | SerialSignal_DTR);
24678c2ecf20Sopenharmony_ci
24688c2ecf20Sopenharmony_ci	/* byte size and parity */
24698c2ecf20Sopenharmony_ci
24708c2ecf20Sopenharmony_ci	switch (cflag & CSIZE) {
24718c2ecf20Sopenharmony_ci	case CS5: info->params.data_bits = 5; break;
24728c2ecf20Sopenharmony_ci	case CS6: info->params.data_bits = 6; break;
24738c2ecf20Sopenharmony_ci	case CS7: info->params.data_bits = 7; break;
24748c2ecf20Sopenharmony_ci	case CS8: info->params.data_bits = 8; break;
24758c2ecf20Sopenharmony_ci	default:  info->params.data_bits = 7; break;
24768c2ecf20Sopenharmony_ci	}
24778c2ecf20Sopenharmony_ci
24788c2ecf20Sopenharmony_ci	info->params.stop_bits = (cflag & CSTOPB) ? 2 : 1;
24798c2ecf20Sopenharmony_ci
24808c2ecf20Sopenharmony_ci	if (cflag & PARENB)
24818c2ecf20Sopenharmony_ci		info->params.parity = (cflag & PARODD) ? ASYNC_PARITY_ODD : ASYNC_PARITY_EVEN;
24828c2ecf20Sopenharmony_ci	else
24838c2ecf20Sopenharmony_ci		info->params.parity = ASYNC_PARITY_NONE;
24848c2ecf20Sopenharmony_ci
24858c2ecf20Sopenharmony_ci	/* calculate number of jiffies to transmit a full
24868c2ecf20Sopenharmony_ci	 * FIFO (32 bytes) at specified data rate
24878c2ecf20Sopenharmony_ci	 */
24888c2ecf20Sopenharmony_ci	bits_per_char = info->params.data_bits +
24898c2ecf20Sopenharmony_ci			info->params.stop_bits + 1;
24908c2ecf20Sopenharmony_ci
24918c2ecf20Sopenharmony_ci	info->params.data_rate = tty_get_baud_rate(info->port.tty);
24928c2ecf20Sopenharmony_ci
24938c2ecf20Sopenharmony_ci	if (info->params.data_rate) {
24948c2ecf20Sopenharmony_ci		info->timeout = (32*HZ*bits_per_char) /
24958c2ecf20Sopenharmony_ci				info->params.data_rate;
24968c2ecf20Sopenharmony_ci	}
24978c2ecf20Sopenharmony_ci	info->timeout += HZ/50;		/* Add .02 seconds of slop */
24988c2ecf20Sopenharmony_ci
24998c2ecf20Sopenharmony_ci	tty_port_set_cts_flow(&info->port, cflag & CRTSCTS);
25008c2ecf20Sopenharmony_ci	tty_port_set_check_carrier(&info->port, ~cflag & CLOCAL);
25018c2ecf20Sopenharmony_ci
25028c2ecf20Sopenharmony_ci	/* process tty input control flags */
25038c2ecf20Sopenharmony_ci
25048c2ecf20Sopenharmony_ci	info->read_status_mask = IRQ_RXOVER;
25058c2ecf20Sopenharmony_ci	if (I_INPCK(info->port.tty))
25068c2ecf20Sopenharmony_ci		info->read_status_mask |= MASK_PARITY | MASK_FRAMING;
25078c2ecf20Sopenharmony_ci	if (I_BRKINT(info->port.tty) || I_PARMRK(info->port.tty))
25088c2ecf20Sopenharmony_ci		info->read_status_mask |= MASK_BREAK;
25098c2ecf20Sopenharmony_ci	if (I_IGNPAR(info->port.tty))
25108c2ecf20Sopenharmony_ci		info->ignore_status_mask |= MASK_PARITY | MASK_FRAMING;
25118c2ecf20Sopenharmony_ci	if (I_IGNBRK(info->port.tty)) {
25128c2ecf20Sopenharmony_ci		info->ignore_status_mask |= MASK_BREAK;
25138c2ecf20Sopenharmony_ci		/* If ignoring parity and break indicators, ignore
25148c2ecf20Sopenharmony_ci		 * overruns too.  (For real raw support).
25158c2ecf20Sopenharmony_ci		 */
25168c2ecf20Sopenharmony_ci		if (I_IGNPAR(info->port.tty))
25178c2ecf20Sopenharmony_ci			info->ignore_status_mask |= MASK_OVERRUN;
25188c2ecf20Sopenharmony_ci	}
25198c2ecf20Sopenharmony_ci
25208c2ecf20Sopenharmony_ci	program_hw(info);
25218c2ecf20Sopenharmony_ci}
25228c2ecf20Sopenharmony_ci
25238c2ecf20Sopenharmony_cistatic int get_stats(struct slgt_info *info, struct mgsl_icount __user *user_icount)
25248c2ecf20Sopenharmony_ci{
25258c2ecf20Sopenharmony_ci	DBGINFO(("%s get_stats\n",  info->device_name));
25268c2ecf20Sopenharmony_ci	if (!user_icount) {
25278c2ecf20Sopenharmony_ci		memset(&info->icount, 0, sizeof(info->icount));
25288c2ecf20Sopenharmony_ci	} else {
25298c2ecf20Sopenharmony_ci		if (copy_to_user(user_icount, &info->icount, sizeof(struct mgsl_icount)))
25308c2ecf20Sopenharmony_ci			return -EFAULT;
25318c2ecf20Sopenharmony_ci	}
25328c2ecf20Sopenharmony_ci	return 0;
25338c2ecf20Sopenharmony_ci}
25348c2ecf20Sopenharmony_ci
25358c2ecf20Sopenharmony_cistatic int get_params(struct slgt_info *info, MGSL_PARAMS __user *user_params)
25368c2ecf20Sopenharmony_ci{
25378c2ecf20Sopenharmony_ci	DBGINFO(("%s get_params\n", info->device_name));
25388c2ecf20Sopenharmony_ci	if (copy_to_user(user_params, &info->params, sizeof(MGSL_PARAMS)))
25398c2ecf20Sopenharmony_ci		return -EFAULT;
25408c2ecf20Sopenharmony_ci	return 0;
25418c2ecf20Sopenharmony_ci}
25428c2ecf20Sopenharmony_ci
25438c2ecf20Sopenharmony_cistatic int set_params(struct slgt_info *info, MGSL_PARAMS __user *new_params)
25448c2ecf20Sopenharmony_ci{
25458c2ecf20Sopenharmony_ci 	unsigned long flags;
25468c2ecf20Sopenharmony_ci	MGSL_PARAMS tmp_params;
25478c2ecf20Sopenharmony_ci
25488c2ecf20Sopenharmony_ci	DBGINFO(("%s set_params\n", info->device_name));
25498c2ecf20Sopenharmony_ci	if (copy_from_user(&tmp_params, new_params, sizeof(MGSL_PARAMS)))
25508c2ecf20Sopenharmony_ci		return -EFAULT;
25518c2ecf20Sopenharmony_ci
25528c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->lock, flags);
25538c2ecf20Sopenharmony_ci	if (tmp_params.mode == MGSL_MODE_BASE_CLOCK)
25548c2ecf20Sopenharmony_ci		info->base_clock = tmp_params.clock_speed;
25558c2ecf20Sopenharmony_ci	else
25568c2ecf20Sopenharmony_ci		memcpy(&info->params, &tmp_params, sizeof(MGSL_PARAMS));
25578c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->lock, flags);
25588c2ecf20Sopenharmony_ci
25598c2ecf20Sopenharmony_ci	program_hw(info);
25608c2ecf20Sopenharmony_ci
25618c2ecf20Sopenharmony_ci	return 0;
25628c2ecf20Sopenharmony_ci}
25638c2ecf20Sopenharmony_ci
25648c2ecf20Sopenharmony_cistatic int get_txidle(struct slgt_info *info, int __user *idle_mode)
25658c2ecf20Sopenharmony_ci{
25668c2ecf20Sopenharmony_ci	DBGINFO(("%s get_txidle=%d\n", info->device_name, info->idle_mode));
25678c2ecf20Sopenharmony_ci	if (put_user(info->idle_mode, idle_mode))
25688c2ecf20Sopenharmony_ci		return -EFAULT;
25698c2ecf20Sopenharmony_ci	return 0;
25708c2ecf20Sopenharmony_ci}
25718c2ecf20Sopenharmony_ci
25728c2ecf20Sopenharmony_cistatic int set_txidle(struct slgt_info *info, int idle_mode)
25738c2ecf20Sopenharmony_ci{
25748c2ecf20Sopenharmony_ci 	unsigned long flags;
25758c2ecf20Sopenharmony_ci	DBGINFO(("%s set_txidle(%d)\n", info->device_name, idle_mode));
25768c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
25778c2ecf20Sopenharmony_ci	info->idle_mode = idle_mode;
25788c2ecf20Sopenharmony_ci	if (info->params.mode != MGSL_MODE_ASYNC)
25798c2ecf20Sopenharmony_ci		tx_set_idle(info);
25808c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
25818c2ecf20Sopenharmony_ci	return 0;
25828c2ecf20Sopenharmony_ci}
25838c2ecf20Sopenharmony_ci
25848c2ecf20Sopenharmony_cistatic int tx_enable(struct slgt_info *info, int enable)
25858c2ecf20Sopenharmony_ci{
25868c2ecf20Sopenharmony_ci 	unsigned long flags;
25878c2ecf20Sopenharmony_ci	DBGINFO(("%s tx_enable(%d)\n", info->device_name, enable));
25888c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
25898c2ecf20Sopenharmony_ci	if (enable) {
25908c2ecf20Sopenharmony_ci		if (!info->tx_enabled)
25918c2ecf20Sopenharmony_ci			tx_start(info);
25928c2ecf20Sopenharmony_ci	} else {
25938c2ecf20Sopenharmony_ci		if (info->tx_enabled)
25948c2ecf20Sopenharmony_ci			tx_stop(info);
25958c2ecf20Sopenharmony_ci	}
25968c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
25978c2ecf20Sopenharmony_ci	return 0;
25988c2ecf20Sopenharmony_ci}
25998c2ecf20Sopenharmony_ci
26008c2ecf20Sopenharmony_ci/*
26018c2ecf20Sopenharmony_ci * abort transmit HDLC frame
26028c2ecf20Sopenharmony_ci */
26038c2ecf20Sopenharmony_cistatic int tx_abort(struct slgt_info *info)
26048c2ecf20Sopenharmony_ci{
26058c2ecf20Sopenharmony_ci 	unsigned long flags;
26068c2ecf20Sopenharmony_ci	DBGINFO(("%s tx_abort\n", info->device_name));
26078c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
26088c2ecf20Sopenharmony_ci	tdma_reset(info);
26098c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
26108c2ecf20Sopenharmony_ci	return 0;
26118c2ecf20Sopenharmony_ci}
26128c2ecf20Sopenharmony_ci
26138c2ecf20Sopenharmony_cistatic int rx_enable(struct slgt_info *info, int enable)
26148c2ecf20Sopenharmony_ci{
26158c2ecf20Sopenharmony_ci 	unsigned long flags;
26168c2ecf20Sopenharmony_ci	unsigned int rbuf_fill_level;
26178c2ecf20Sopenharmony_ci	DBGINFO(("%s rx_enable(%08x)\n", info->device_name, enable));
26188c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
26198c2ecf20Sopenharmony_ci	/*
26208c2ecf20Sopenharmony_ci	 * enable[31..16] = receive DMA buffer fill level
26218c2ecf20Sopenharmony_ci	 * 0 = noop (leave fill level unchanged)
26228c2ecf20Sopenharmony_ci	 * fill level must be multiple of 4 and <= buffer size
26238c2ecf20Sopenharmony_ci	 */
26248c2ecf20Sopenharmony_ci	rbuf_fill_level = ((unsigned int)enable) >> 16;
26258c2ecf20Sopenharmony_ci	if (rbuf_fill_level) {
26268c2ecf20Sopenharmony_ci		if ((rbuf_fill_level > DMABUFSIZE) || (rbuf_fill_level % 4)) {
26278c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&info->lock, flags);
26288c2ecf20Sopenharmony_ci			return -EINVAL;
26298c2ecf20Sopenharmony_ci		}
26308c2ecf20Sopenharmony_ci		info->rbuf_fill_level = rbuf_fill_level;
26318c2ecf20Sopenharmony_ci		if (rbuf_fill_level < 128)
26328c2ecf20Sopenharmony_ci			info->rx_pio = 1; /* PIO mode */
26338c2ecf20Sopenharmony_ci		else
26348c2ecf20Sopenharmony_ci			info->rx_pio = 0; /* DMA mode */
26358c2ecf20Sopenharmony_ci		rx_stop(info); /* restart receiver to use new fill level */
26368c2ecf20Sopenharmony_ci	}
26378c2ecf20Sopenharmony_ci
26388c2ecf20Sopenharmony_ci	/*
26398c2ecf20Sopenharmony_ci	 * enable[1..0] = receiver enable command
26408c2ecf20Sopenharmony_ci	 * 0 = disable
26418c2ecf20Sopenharmony_ci	 * 1 = enable
26428c2ecf20Sopenharmony_ci	 * 2 = enable or force hunt mode if already enabled
26438c2ecf20Sopenharmony_ci	 */
26448c2ecf20Sopenharmony_ci	enable &= 3;
26458c2ecf20Sopenharmony_ci	if (enable) {
26468c2ecf20Sopenharmony_ci		if (!info->rx_enabled)
26478c2ecf20Sopenharmony_ci			rx_start(info);
26488c2ecf20Sopenharmony_ci		else if (enable == 2) {
26498c2ecf20Sopenharmony_ci			/* force hunt mode (write 1 to RCR[3]) */
26508c2ecf20Sopenharmony_ci			wr_reg16(info, RCR, rd_reg16(info, RCR) | BIT3);
26518c2ecf20Sopenharmony_ci		}
26528c2ecf20Sopenharmony_ci	} else {
26538c2ecf20Sopenharmony_ci		if (info->rx_enabled)
26548c2ecf20Sopenharmony_ci			rx_stop(info);
26558c2ecf20Sopenharmony_ci	}
26568c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
26578c2ecf20Sopenharmony_ci	return 0;
26588c2ecf20Sopenharmony_ci}
26598c2ecf20Sopenharmony_ci
26608c2ecf20Sopenharmony_ci/*
26618c2ecf20Sopenharmony_ci *  wait for specified event to occur
26628c2ecf20Sopenharmony_ci */
26638c2ecf20Sopenharmony_cistatic int wait_mgsl_event(struct slgt_info *info, int __user *mask_ptr)
26648c2ecf20Sopenharmony_ci{
26658c2ecf20Sopenharmony_ci 	unsigned long flags;
26668c2ecf20Sopenharmony_ci	int s;
26678c2ecf20Sopenharmony_ci	int rc=0;
26688c2ecf20Sopenharmony_ci	struct mgsl_icount cprev, cnow;
26698c2ecf20Sopenharmony_ci	int events;
26708c2ecf20Sopenharmony_ci	int mask;
26718c2ecf20Sopenharmony_ci	struct	_input_signal_events oldsigs, newsigs;
26728c2ecf20Sopenharmony_ci	DECLARE_WAITQUEUE(wait, current);
26738c2ecf20Sopenharmony_ci
26748c2ecf20Sopenharmony_ci	if (get_user(mask, mask_ptr))
26758c2ecf20Sopenharmony_ci		return -EFAULT;
26768c2ecf20Sopenharmony_ci
26778c2ecf20Sopenharmony_ci	DBGINFO(("%s wait_mgsl_event(%d)\n", info->device_name, mask));
26788c2ecf20Sopenharmony_ci
26798c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
26808c2ecf20Sopenharmony_ci
26818c2ecf20Sopenharmony_ci	/* return immediately if state matches requested events */
26828c2ecf20Sopenharmony_ci	get_gtsignals(info);
26838c2ecf20Sopenharmony_ci	s = info->signals;
26848c2ecf20Sopenharmony_ci
26858c2ecf20Sopenharmony_ci	events = mask &
26868c2ecf20Sopenharmony_ci		( ((s & SerialSignal_DSR) ? MgslEvent_DsrActive:MgslEvent_DsrInactive) +
26878c2ecf20Sopenharmony_ci 		  ((s & SerialSignal_DCD) ? MgslEvent_DcdActive:MgslEvent_DcdInactive) +
26888c2ecf20Sopenharmony_ci		  ((s & SerialSignal_CTS) ? MgslEvent_CtsActive:MgslEvent_CtsInactive) +
26898c2ecf20Sopenharmony_ci		  ((s & SerialSignal_RI)  ? MgslEvent_RiActive :MgslEvent_RiInactive) );
26908c2ecf20Sopenharmony_ci	if (events) {
26918c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&info->lock,flags);
26928c2ecf20Sopenharmony_ci		goto exit;
26938c2ecf20Sopenharmony_ci	}
26948c2ecf20Sopenharmony_ci
26958c2ecf20Sopenharmony_ci	/* save current irq counts */
26968c2ecf20Sopenharmony_ci	cprev = info->icount;
26978c2ecf20Sopenharmony_ci	oldsigs = info->input_signal_events;
26988c2ecf20Sopenharmony_ci
26998c2ecf20Sopenharmony_ci	/* enable hunt and idle irqs if needed */
27008c2ecf20Sopenharmony_ci	if (mask & (MgslEvent_ExitHuntMode+MgslEvent_IdleReceived)) {
27018c2ecf20Sopenharmony_ci		unsigned short val = rd_reg16(info, SCR);
27028c2ecf20Sopenharmony_ci		if (!(val & IRQ_RXIDLE))
27038c2ecf20Sopenharmony_ci			wr_reg16(info, SCR, (unsigned short)(val | IRQ_RXIDLE));
27048c2ecf20Sopenharmony_ci	}
27058c2ecf20Sopenharmony_ci
27068c2ecf20Sopenharmony_ci	set_current_state(TASK_INTERRUPTIBLE);
27078c2ecf20Sopenharmony_ci	add_wait_queue(&info->event_wait_q, &wait);
27088c2ecf20Sopenharmony_ci
27098c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
27108c2ecf20Sopenharmony_ci
27118c2ecf20Sopenharmony_ci	for(;;) {
27128c2ecf20Sopenharmony_ci		schedule();
27138c2ecf20Sopenharmony_ci		if (signal_pending(current)) {
27148c2ecf20Sopenharmony_ci			rc = -ERESTARTSYS;
27158c2ecf20Sopenharmony_ci			break;
27168c2ecf20Sopenharmony_ci		}
27178c2ecf20Sopenharmony_ci
27188c2ecf20Sopenharmony_ci		/* get current irq counts */
27198c2ecf20Sopenharmony_ci		spin_lock_irqsave(&info->lock,flags);
27208c2ecf20Sopenharmony_ci		cnow = info->icount;
27218c2ecf20Sopenharmony_ci		newsigs = info->input_signal_events;
27228c2ecf20Sopenharmony_ci		set_current_state(TASK_INTERRUPTIBLE);
27238c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&info->lock,flags);
27248c2ecf20Sopenharmony_ci
27258c2ecf20Sopenharmony_ci		/* if no change, wait aborted for some reason */
27268c2ecf20Sopenharmony_ci		if (newsigs.dsr_up   == oldsigs.dsr_up   &&
27278c2ecf20Sopenharmony_ci		    newsigs.dsr_down == oldsigs.dsr_down &&
27288c2ecf20Sopenharmony_ci		    newsigs.dcd_up   == oldsigs.dcd_up   &&
27298c2ecf20Sopenharmony_ci		    newsigs.dcd_down == oldsigs.dcd_down &&
27308c2ecf20Sopenharmony_ci		    newsigs.cts_up   == oldsigs.cts_up   &&
27318c2ecf20Sopenharmony_ci		    newsigs.cts_down == oldsigs.cts_down &&
27328c2ecf20Sopenharmony_ci		    newsigs.ri_up    == oldsigs.ri_up    &&
27338c2ecf20Sopenharmony_ci		    newsigs.ri_down  == oldsigs.ri_down  &&
27348c2ecf20Sopenharmony_ci		    cnow.exithunt    == cprev.exithunt   &&
27358c2ecf20Sopenharmony_ci		    cnow.rxidle      == cprev.rxidle) {
27368c2ecf20Sopenharmony_ci			rc = -EIO;
27378c2ecf20Sopenharmony_ci			break;
27388c2ecf20Sopenharmony_ci		}
27398c2ecf20Sopenharmony_ci
27408c2ecf20Sopenharmony_ci		events = mask &
27418c2ecf20Sopenharmony_ci			( (newsigs.dsr_up   != oldsigs.dsr_up   ? MgslEvent_DsrActive:0)   +
27428c2ecf20Sopenharmony_ci			  (newsigs.dsr_down != oldsigs.dsr_down ? MgslEvent_DsrInactive:0) +
27438c2ecf20Sopenharmony_ci			  (newsigs.dcd_up   != oldsigs.dcd_up   ? MgslEvent_DcdActive:0)   +
27448c2ecf20Sopenharmony_ci			  (newsigs.dcd_down != oldsigs.dcd_down ? MgslEvent_DcdInactive:0) +
27458c2ecf20Sopenharmony_ci			  (newsigs.cts_up   != oldsigs.cts_up   ? MgslEvent_CtsActive:0)   +
27468c2ecf20Sopenharmony_ci			  (newsigs.cts_down != oldsigs.cts_down ? MgslEvent_CtsInactive:0) +
27478c2ecf20Sopenharmony_ci			  (newsigs.ri_up    != oldsigs.ri_up    ? MgslEvent_RiActive:0)    +
27488c2ecf20Sopenharmony_ci			  (newsigs.ri_down  != oldsigs.ri_down  ? MgslEvent_RiInactive:0)  +
27498c2ecf20Sopenharmony_ci			  (cnow.exithunt    != cprev.exithunt   ? MgslEvent_ExitHuntMode:0) +
27508c2ecf20Sopenharmony_ci			  (cnow.rxidle      != cprev.rxidle     ? MgslEvent_IdleReceived:0) );
27518c2ecf20Sopenharmony_ci		if (events)
27528c2ecf20Sopenharmony_ci			break;
27538c2ecf20Sopenharmony_ci
27548c2ecf20Sopenharmony_ci		cprev = cnow;
27558c2ecf20Sopenharmony_ci		oldsigs = newsigs;
27568c2ecf20Sopenharmony_ci	}
27578c2ecf20Sopenharmony_ci
27588c2ecf20Sopenharmony_ci	remove_wait_queue(&info->event_wait_q, &wait);
27598c2ecf20Sopenharmony_ci	set_current_state(TASK_RUNNING);
27608c2ecf20Sopenharmony_ci
27618c2ecf20Sopenharmony_ci
27628c2ecf20Sopenharmony_ci	if (mask & (MgslEvent_ExitHuntMode + MgslEvent_IdleReceived)) {
27638c2ecf20Sopenharmony_ci		spin_lock_irqsave(&info->lock,flags);
27648c2ecf20Sopenharmony_ci		if (!waitqueue_active(&info->event_wait_q)) {
27658c2ecf20Sopenharmony_ci			/* disable enable exit hunt mode/idle rcvd IRQs */
27668c2ecf20Sopenharmony_ci			wr_reg16(info, SCR,
27678c2ecf20Sopenharmony_ci				(unsigned short)(rd_reg16(info, SCR) & ~IRQ_RXIDLE));
27688c2ecf20Sopenharmony_ci		}
27698c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&info->lock,flags);
27708c2ecf20Sopenharmony_ci	}
27718c2ecf20Sopenharmony_ciexit:
27728c2ecf20Sopenharmony_ci	if (rc == 0)
27738c2ecf20Sopenharmony_ci		rc = put_user(events, mask_ptr);
27748c2ecf20Sopenharmony_ci	return rc;
27758c2ecf20Sopenharmony_ci}
27768c2ecf20Sopenharmony_ci
27778c2ecf20Sopenharmony_cistatic int get_interface(struct slgt_info *info, int __user *if_mode)
27788c2ecf20Sopenharmony_ci{
27798c2ecf20Sopenharmony_ci	DBGINFO(("%s get_interface=%x\n", info->device_name, info->if_mode));
27808c2ecf20Sopenharmony_ci	if (put_user(info->if_mode, if_mode))
27818c2ecf20Sopenharmony_ci		return -EFAULT;
27828c2ecf20Sopenharmony_ci	return 0;
27838c2ecf20Sopenharmony_ci}
27848c2ecf20Sopenharmony_ci
27858c2ecf20Sopenharmony_cistatic int set_interface(struct slgt_info *info, int if_mode)
27868c2ecf20Sopenharmony_ci{
27878c2ecf20Sopenharmony_ci 	unsigned long flags;
27888c2ecf20Sopenharmony_ci	unsigned short val;
27898c2ecf20Sopenharmony_ci
27908c2ecf20Sopenharmony_ci	DBGINFO(("%s set_interface=%x)\n", info->device_name, if_mode));
27918c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
27928c2ecf20Sopenharmony_ci	info->if_mode = if_mode;
27938c2ecf20Sopenharmony_ci
27948c2ecf20Sopenharmony_ci	msc_set_vcr(info);
27958c2ecf20Sopenharmony_ci
27968c2ecf20Sopenharmony_ci	/* TCR (tx control) 07  1=RTS driver control */
27978c2ecf20Sopenharmony_ci	val = rd_reg16(info, TCR);
27988c2ecf20Sopenharmony_ci	if (info->if_mode & MGSL_INTERFACE_RTS_EN)
27998c2ecf20Sopenharmony_ci		val |= BIT7;
28008c2ecf20Sopenharmony_ci	else
28018c2ecf20Sopenharmony_ci		val &= ~BIT7;
28028c2ecf20Sopenharmony_ci	wr_reg16(info, TCR, val);
28038c2ecf20Sopenharmony_ci
28048c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
28058c2ecf20Sopenharmony_ci	return 0;
28068c2ecf20Sopenharmony_ci}
28078c2ecf20Sopenharmony_ci
28088c2ecf20Sopenharmony_cistatic int get_xsync(struct slgt_info *info, int __user *xsync)
28098c2ecf20Sopenharmony_ci{
28108c2ecf20Sopenharmony_ci	DBGINFO(("%s get_xsync=%x\n", info->device_name, info->xsync));
28118c2ecf20Sopenharmony_ci	if (put_user(info->xsync, xsync))
28128c2ecf20Sopenharmony_ci		return -EFAULT;
28138c2ecf20Sopenharmony_ci	return 0;
28148c2ecf20Sopenharmony_ci}
28158c2ecf20Sopenharmony_ci
28168c2ecf20Sopenharmony_ci/*
28178c2ecf20Sopenharmony_ci * set extended sync pattern (1 to 4 bytes) for extended sync mode
28188c2ecf20Sopenharmony_ci *
28198c2ecf20Sopenharmony_ci * sync pattern is contained in least significant bytes of value
28208c2ecf20Sopenharmony_ci * most significant byte of sync pattern is oldest (1st sent/detected)
28218c2ecf20Sopenharmony_ci */
28228c2ecf20Sopenharmony_cistatic int set_xsync(struct slgt_info *info, int xsync)
28238c2ecf20Sopenharmony_ci{
28248c2ecf20Sopenharmony_ci	unsigned long flags;
28258c2ecf20Sopenharmony_ci
28268c2ecf20Sopenharmony_ci	DBGINFO(("%s set_xsync=%x)\n", info->device_name, xsync));
28278c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->lock, flags);
28288c2ecf20Sopenharmony_ci	info->xsync = xsync;
28298c2ecf20Sopenharmony_ci	wr_reg32(info, XSR, xsync);
28308c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->lock, flags);
28318c2ecf20Sopenharmony_ci	return 0;
28328c2ecf20Sopenharmony_ci}
28338c2ecf20Sopenharmony_ci
28348c2ecf20Sopenharmony_cistatic int get_xctrl(struct slgt_info *info, int __user *xctrl)
28358c2ecf20Sopenharmony_ci{
28368c2ecf20Sopenharmony_ci	DBGINFO(("%s get_xctrl=%x\n", info->device_name, info->xctrl));
28378c2ecf20Sopenharmony_ci	if (put_user(info->xctrl, xctrl))
28388c2ecf20Sopenharmony_ci		return -EFAULT;
28398c2ecf20Sopenharmony_ci	return 0;
28408c2ecf20Sopenharmony_ci}
28418c2ecf20Sopenharmony_ci
28428c2ecf20Sopenharmony_ci/*
28438c2ecf20Sopenharmony_ci * set extended control options
28448c2ecf20Sopenharmony_ci *
28458c2ecf20Sopenharmony_ci * xctrl[31:19] reserved, must be zero
28468c2ecf20Sopenharmony_ci * xctrl[18:17] extended sync pattern length in bytes
28478c2ecf20Sopenharmony_ci *              00 = 1 byte  in xsr[7:0]
28488c2ecf20Sopenharmony_ci *              01 = 2 bytes in xsr[15:0]
28498c2ecf20Sopenharmony_ci *              10 = 3 bytes in xsr[23:0]
28508c2ecf20Sopenharmony_ci *              11 = 4 bytes in xsr[31:0]
28518c2ecf20Sopenharmony_ci * xctrl[16]    1 = enable terminal count, 0=disabled
28528c2ecf20Sopenharmony_ci * xctrl[15:0]  receive terminal count for fixed length packets
28538c2ecf20Sopenharmony_ci *              value is count minus one (0 = 1 byte packet)
28548c2ecf20Sopenharmony_ci *              when terminal count is reached, receiver
28558c2ecf20Sopenharmony_ci *              automatically returns to hunt mode and receive
28568c2ecf20Sopenharmony_ci *              FIFO contents are flushed to DMA buffers with
28578c2ecf20Sopenharmony_ci *              end of frame (EOF) status
28588c2ecf20Sopenharmony_ci */
28598c2ecf20Sopenharmony_cistatic int set_xctrl(struct slgt_info *info, int xctrl)
28608c2ecf20Sopenharmony_ci{
28618c2ecf20Sopenharmony_ci	unsigned long flags;
28628c2ecf20Sopenharmony_ci
28638c2ecf20Sopenharmony_ci	DBGINFO(("%s set_xctrl=%x)\n", info->device_name, xctrl));
28648c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->lock, flags);
28658c2ecf20Sopenharmony_ci	info->xctrl = xctrl;
28668c2ecf20Sopenharmony_ci	wr_reg32(info, XCR, xctrl);
28678c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->lock, flags);
28688c2ecf20Sopenharmony_ci	return 0;
28698c2ecf20Sopenharmony_ci}
28708c2ecf20Sopenharmony_ci
28718c2ecf20Sopenharmony_ci/*
28728c2ecf20Sopenharmony_ci * set general purpose IO pin state and direction
28738c2ecf20Sopenharmony_ci *
28748c2ecf20Sopenharmony_ci * user_gpio fields:
28758c2ecf20Sopenharmony_ci * state   each bit indicates a pin state
28768c2ecf20Sopenharmony_ci * smask   set bit indicates pin state to set
28778c2ecf20Sopenharmony_ci * dir     each bit indicates a pin direction (0=input, 1=output)
28788c2ecf20Sopenharmony_ci * dmask   set bit indicates pin direction to set
28798c2ecf20Sopenharmony_ci */
28808c2ecf20Sopenharmony_cistatic int set_gpio(struct slgt_info *info, struct gpio_desc __user *user_gpio)
28818c2ecf20Sopenharmony_ci{
28828c2ecf20Sopenharmony_ci 	unsigned long flags;
28838c2ecf20Sopenharmony_ci	struct gpio_desc gpio;
28848c2ecf20Sopenharmony_ci	__u32 data;
28858c2ecf20Sopenharmony_ci
28868c2ecf20Sopenharmony_ci	if (!info->gpio_present)
28878c2ecf20Sopenharmony_ci		return -EINVAL;
28888c2ecf20Sopenharmony_ci	if (copy_from_user(&gpio, user_gpio, sizeof(gpio)))
28898c2ecf20Sopenharmony_ci		return -EFAULT;
28908c2ecf20Sopenharmony_ci	DBGINFO(("%s set_gpio state=%08x smask=%08x dir=%08x dmask=%08x\n",
28918c2ecf20Sopenharmony_ci		 info->device_name, gpio.state, gpio.smask,
28928c2ecf20Sopenharmony_ci		 gpio.dir, gpio.dmask));
28938c2ecf20Sopenharmony_ci
28948c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->port_array[0]->lock, flags);
28958c2ecf20Sopenharmony_ci	if (gpio.dmask) {
28968c2ecf20Sopenharmony_ci		data = rd_reg32(info, IODR);
28978c2ecf20Sopenharmony_ci		data |= gpio.dmask & gpio.dir;
28988c2ecf20Sopenharmony_ci		data &= ~(gpio.dmask & ~gpio.dir);
28998c2ecf20Sopenharmony_ci		wr_reg32(info, IODR, data);
29008c2ecf20Sopenharmony_ci	}
29018c2ecf20Sopenharmony_ci	if (gpio.smask) {
29028c2ecf20Sopenharmony_ci		data = rd_reg32(info, IOVR);
29038c2ecf20Sopenharmony_ci		data |= gpio.smask & gpio.state;
29048c2ecf20Sopenharmony_ci		data &= ~(gpio.smask & ~gpio.state);
29058c2ecf20Sopenharmony_ci		wr_reg32(info, IOVR, data);
29068c2ecf20Sopenharmony_ci	}
29078c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->port_array[0]->lock, flags);
29088c2ecf20Sopenharmony_ci
29098c2ecf20Sopenharmony_ci	return 0;
29108c2ecf20Sopenharmony_ci}
29118c2ecf20Sopenharmony_ci
29128c2ecf20Sopenharmony_ci/*
29138c2ecf20Sopenharmony_ci * get general purpose IO pin state and direction
29148c2ecf20Sopenharmony_ci */
29158c2ecf20Sopenharmony_cistatic int get_gpio(struct slgt_info *info, struct gpio_desc __user *user_gpio)
29168c2ecf20Sopenharmony_ci{
29178c2ecf20Sopenharmony_ci	struct gpio_desc gpio;
29188c2ecf20Sopenharmony_ci	if (!info->gpio_present)
29198c2ecf20Sopenharmony_ci		return -EINVAL;
29208c2ecf20Sopenharmony_ci	gpio.state = rd_reg32(info, IOVR);
29218c2ecf20Sopenharmony_ci	gpio.smask = 0xffffffff;
29228c2ecf20Sopenharmony_ci	gpio.dir   = rd_reg32(info, IODR);
29238c2ecf20Sopenharmony_ci	gpio.dmask = 0xffffffff;
29248c2ecf20Sopenharmony_ci	if (copy_to_user(user_gpio, &gpio, sizeof(gpio)))
29258c2ecf20Sopenharmony_ci		return -EFAULT;
29268c2ecf20Sopenharmony_ci	DBGINFO(("%s get_gpio state=%08x dir=%08x\n",
29278c2ecf20Sopenharmony_ci		 info->device_name, gpio.state, gpio.dir));
29288c2ecf20Sopenharmony_ci	return 0;
29298c2ecf20Sopenharmony_ci}
29308c2ecf20Sopenharmony_ci
29318c2ecf20Sopenharmony_ci/*
29328c2ecf20Sopenharmony_ci * conditional wait facility
29338c2ecf20Sopenharmony_ci */
29348c2ecf20Sopenharmony_cistatic void init_cond_wait(struct cond_wait *w, unsigned int data)
29358c2ecf20Sopenharmony_ci{
29368c2ecf20Sopenharmony_ci	init_waitqueue_head(&w->q);
29378c2ecf20Sopenharmony_ci	init_waitqueue_entry(&w->wait, current);
29388c2ecf20Sopenharmony_ci	w->data = data;
29398c2ecf20Sopenharmony_ci}
29408c2ecf20Sopenharmony_ci
29418c2ecf20Sopenharmony_cistatic void add_cond_wait(struct cond_wait **head, struct cond_wait *w)
29428c2ecf20Sopenharmony_ci{
29438c2ecf20Sopenharmony_ci	set_current_state(TASK_INTERRUPTIBLE);
29448c2ecf20Sopenharmony_ci	add_wait_queue(&w->q, &w->wait);
29458c2ecf20Sopenharmony_ci	w->next = *head;
29468c2ecf20Sopenharmony_ci	*head = w;
29478c2ecf20Sopenharmony_ci}
29488c2ecf20Sopenharmony_ci
29498c2ecf20Sopenharmony_cistatic void remove_cond_wait(struct cond_wait **head, struct cond_wait *cw)
29508c2ecf20Sopenharmony_ci{
29518c2ecf20Sopenharmony_ci	struct cond_wait *w, *prev;
29528c2ecf20Sopenharmony_ci	remove_wait_queue(&cw->q, &cw->wait);
29538c2ecf20Sopenharmony_ci	set_current_state(TASK_RUNNING);
29548c2ecf20Sopenharmony_ci	for (w = *head, prev = NULL ; w != NULL ; prev = w, w = w->next) {
29558c2ecf20Sopenharmony_ci		if (w == cw) {
29568c2ecf20Sopenharmony_ci			if (prev != NULL)
29578c2ecf20Sopenharmony_ci				prev->next = w->next;
29588c2ecf20Sopenharmony_ci			else
29598c2ecf20Sopenharmony_ci				*head = w->next;
29608c2ecf20Sopenharmony_ci			break;
29618c2ecf20Sopenharmony_ci		}
29628c2ecf20Sopenharmony_ci	}
29638c2ecf20Sopenharmony_ci}
29648c2ecf20Sopenharmony_ci
29658c2ecf20Sopenharmony_cistatic void flush_cond_wait(struct cond_wait **head)
29668c2ecf20Sopenharmony_ci{
29678c2ecf20Sopenharmony_ci	while (*head != NULL) {
29688c2ecf20Sopenharmony_ci		wake_up_interruptible(&(*head)->q);
29698c2ecf20Sopenharmony_ci		*head = (*head)->next;
29708c2ecf20Sopenharmony_ci	}
29718c2ecf20Sopenharmony_ci}
29728c2ecf20Sopenharmony_ci
29738c2ecf20Sopenharmony_ci/*
29748c2ecf20Sopenharmony_ci * wait for general purpose I/O pin(s) to enter specified state
29758c2ecf20Sopenharmony_ci *
29768c2ecf20Sopenharmony_ci * user_gpio fields:
29778c2ecf20Sopenharmony_ci * state - bit indicates target pin state
29788c2ecf20Sopenharmony_ci * smask - set bit indicates watched pin
29798c2ecf20Sopenharmony_ci *
29808c2ecf20Sopenharmony_ci * The wait ends when at least one watched pin enters the specified
29818c2ecf20Sopenharmony_ci * state. When 0 (no error) is returned, user_gpio->state is set to the
29828c2ecf20Sopenharmony_ci * state of all GPIO pins when the wait ends.
29838c2ecf20Sopenharmony_ci *
29848c2ecf20Sopenharmony_ci * Note: Each pin may be a dedicated input, dedicated output, or
29858c2ecf20Sopenharmony_ci * configurable input/output. The number and configuration of pins
29868c2ecf20Sopenharmony_ci * varies with the specific adapter model. Only input pins (dedicated
29878c2ecf20Sopenharmony_ci * or configured) can be monitored with this function.
29888c2ecf20Sopenharmony_ci */
29898c2ecf20Sopenharmony_cistatic int wait_gpio(struct slgt_info *info, struct gpio_desc __user *user_gpio)
29908c2ecf20Sopenharmony_ci{
29918c2ecf20Sopenharmony_ci 	unsigned long flags;
29928c2ecf20Sopenharmony_ci	int rc = 0;
29938c2ecf20Sopenharmony_ci	struct gpio_desc gpio;
29948c2ecf20Sopenharmony_ci	struct cond_wait wait;
29958c2ecf20Sopenharmony_ci	u32 state;
29968c2ecf20Sopenharmony_ci
29978c2ecf20Sopenharmony_ci	if (!info->gpio_present)
29988c2ecf20Sopenharmony_ci		return -EINVAL;
29998c2ecf20Sopenharmony_ci	if (copy_from_user(&gpio, user_gpio, sizeof(gpio)))
30008c2ecf20Sopenharmony_ci		return -EFAULT;
30018c2ecf20Sopenharmony_ci	DBGINFO(("%s wait_gpio() state=%08x smask=%08x\n",
30028c2ecf20Sopenharmony_ci		 info->device_name, gpio.state, gpio.smask));
30038c2ecf20Sopenharmony_ci	/* ignore output pins identified by set IODR bit */
30048c2ecf20Sopenharmony_ci	if ((gpio.smask &= ~rd_reg32(info, IODR)) == 0)
30058c2ecf20Sopenharmony_ci		return -EINVAL;
30068c2ecf20Sopenharmony_ci	init_cond_wait(&wait, gpio.smask);
30078c2ecf20Sopenharmony_ci
30088c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->port_array[0]->lock, flags);
30098c2ecf20Sopenharmony_ci	/* enable interrupts for watched pins */
30108c2ecf20Sopenharmony_ci	wr_reg32(info, IOER, rd_reg32(info, IOER) | gpio.smask);
30118c2ecf20Sopenharmony_ci	/* get current pin states */
30128c2ecf20Sopenharmony_ci	state = rd_reg32(info, IOVR);
30138c2ecf20Sopenharmony_ci
30148c2ecf20Sopenharmony_ci	if (gpio.smask & ~(state ^ gpio.state)) {
30158c2ecf20Sopenharmony_ci		/* already in target state */
30168c2ecf20Sopenharmony_ci		gpio.state = state;
30178c2ecf20Sopenharmony_ci	} else {
30188c2ecf20Sopenharmony_ci		/* wait for target state */
30198c2ecf20Sopenharmony_ci		add_cond_wait(&info->gpio_wait_q, &wait);
30208c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&info->port_array[0]->lock, flags);
30218c2ecf20Sopenharmony_ci		schedule();
30228c2ecf20Sopenharmony_ci		if (signal_pending(current))
30238c2ecf20Sopenharmony_ci			rc = -ERESTARTSYS;
30248c2ecf20Sopenharmony_ci		else
30258c2ecf20Sopenharmony_ci			gpio.state = wait.data;
30268c2ecf20Sopenharmony_ci		spin_lock_irqsave(&info->port_array[0]->lock, flags);
30278c2ecf20Sopenharmony_ci		remove_cond_wait(&info->gpio_wait_q, &wait);
30288c2ecf20Sopenharmony_ci	}
30298c2ecf20Sopenharmony_ci
30308c2ecf20Sopenharmony_ci	/* disable all GPIO interrupts if no waiting processes */
30318c2ecf20Sopenharmony_ci	if (info->gpio_wait_q == NULL)
30328c2ecf20Sopenharmony_ci		wr_reg32(info, IOER, 0);
30338c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->port_array[0]->lock, flags);
30348c2ecf20Sopenharmony_ci
30358c2ecf20Sopenharmony_ci	if ((rc == 0) && copy_to_user(user_gpio, &gpio, sizeof(gpio)))
30368c2ecf20Sopenharmony_ci		rc = -EFAULT;
30378c2ecf20Sopenharmony_ci	return rc;
30388c2ecf20Sopenharmony_ci}
30398c2ecf20Sopenharmony_ci
30408c2ecf20Sopenharmony_cistatic int modem_input_wait(struct slgt_info *info,int arg)
30418c2ecf20Sopenharmony_ci{
30428c2ecf20Sopenharmony_ci 	unsigned long flags;
30438c2ecf20Sopenharmony_ci	int rc;
30448c2ecf20Sopenharmony_ci	struct mgsl_icount cprev, cnow;
30458c2ecf20Sopenharmony_ci	DECLARE_WAITQUEUE(wait, current);
30468c2ecf20Sopenharmony_ci
30478c2ecf20Sopenharmony_ci	/* save current irq counts */
30488c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
30498c2ecf20Sopenharmony_ci	cprev = info->icount;
30508c2ecf20Sopenharmony_ci	add_wait_queue(&info->status_event_wait_q, &wait);
30518c2ecf20Sopenharmony_ci	set_current_state(TASK_INTERRUPTIBLE);
30528c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
30538c2ecf20Sopenharmony_ci
30548c2ecf20Sopenharmony_ci	for(;;) {
30558c2ecf20Sopenharmony_ci		schedule();
30568c2ecf20Sopenharmony_ci		if (signal_pending(current)) {
30578c2ecf20Sopenharmony_ci			rc = -ERESTARTSYS;
30588c2ecf20Sopenharmony_ci			break;
30598c2ecf20Sopenharmony_ci		}
30608c2ecf20Sopenharmony_ci
30618c2ecf20Sopenharmony_ci		/* get new irq counts */
30628c2ecf20Sopenharmony_ci		spin_lock_irqsave(&info->lock,flags);
30638c2ecf20Sopenharmony_ci		cnow = info->icount;
30648c2ecf20Sopenharmony_ci		set_current_state(TASK_INTERRUPTIBLE);
30658c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&info->lock,flags);
30668c2ecf20Sopenharmony_ci
30678c2ecf20Sopenharmony_ci		/* if no change, wait aborted for some reason */
30688c2ecf20Sopenharmony_ci		if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
30698c2ecf20Sopenharmony_ci		    cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) {
30708c2ecf20Sopenharmony_ci			rc = -EIO;
30718c2ecf20Sopenharmony_ci			break;
30728c2ecf20Sopenharmony_ci		}
30738c2ecf20Sopenharmony_ci
30748c2ecf20Sopenharmony_ci		/* check for change in caller specified modem input */
30758c2ecf20Sopenharmony_ci		if ((arg & TIOCM_RNG && cnow.rng != cprev.rng) ||
30768c2ecf20Sopenharmony_ci		    (arg & TIOCM_DSR && cnow.dsr != cprev.dsr) ||
30778c2ecf20Sopenharmony_ci		    (arg & TIOCM_CD  && cnow.dcd != cprev.dcd) ||
30788c2ecf20Sopenharmony_ci		    (arg & TIOCM_CTS && cnow.cts != cprev.cts)) {
30798c2ecf20Sopenharmony_ci			rc = 0;
30808c2ecf20Sopenharmony_ci			break;
30818c2ecf20Sopenharmony_ci		}
30828c2ecf20Sopenharmony_ci
30838c2ecf20Sopenharmony_ci		cprev = cnow;
30848c2ecf20Sopenharmony_ci	}
30858c2ecf20Sopenharmony_ci	remove_wait_queue(&info->status_event_wait_q, &wait);
30868c2ecf20Sopenharmony_ci	set_current_state(TASK_RUNNING);
30878c2ecf20Sopenharmony_ci	return rc;
30888c2ecf20Sopenharmony_ci}
30898c2ecf20Sopenharmony_ci
30908c2ecf20Sopenharmony_ci/*
30918c2ecf20Sopenharmony_ci *  return state of serial control and status signals
30928c2ecf20Sopenharmony_ci */
30938c2ecf20Sopenharmony_cistatic int tiocmget(struct tty_struct *tty)
30948c2ecf20Sopenharmony_ci{
30958c2ecf20Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
30968c2ecf20Sopenharmony_ci	unsigned int result;
30978c2ecf20Sopenharmony_ci 	unsigned long flags;
30988c2ecf20Sopenharmony_ci
30998c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
31008c2ecf20Sopenharmony_ci 	get_gtsignals(info);
31018c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
31028c2ecf20Sopenharmony_ci
31038c2ecf20Sopenharmony_ci	result = ((info->signals & SerialSignal_RTS) ? TIOCM_RTS:0) +
31048c2ecf20Sopenharmony_ci		((info->signals & SerialSignal_DTR) ? TIOCM_DTR:0) +
31058c2ecf20Sopenharmony_ci		((info->signals & SerialSignal_DCD) ? TIOCM_CAR:0) +
31068c2ecf20Sopenharmony_ci		((info->signals & SerialSignal_RI)  ? TIOCM_RNG:0) +
31078c2ecf20Sopenharmony_ci		((info->signals & SerialSignal_DSR) ? TIOCM_DSR:0) +
31088c2ecf20Sopenharmony_ci		((info->signals & SerialSignal_CTS) ? TIOCM_CTS:0);
31098c2ecf20Sopenharmony_ci
31108c2ecf20Sopenharmony_ci	DBGINFO(("%s tiocmget value=%08X\n", info->device_name, result));
31118c2ecf20Sopenharmony_ci	return result;
31128c2ecf20Sopenharmony_ci}
31138c2ecf20Sopenharmony_ci
31148c2ecf20Sopenharmony_ci/*
31158c2ecf20Sopenharmony_ci * set modem control signals (DTR/RTS)
31168c2ecf20Sopenharmony_ci *
31178c2ecf20Sopenharmony_ci * 	cmd	signal command: TIOCMBIS = set bit TIOCMBIC = clear bit
31188c2ecf20Sopenharmony_ci *		TIOCMSET = set/clear signal values
31198c2ecf20Sopenharmony_ci * 	value	bit mask for command
31208c2ecf20Sopenharmony_ci */
31218c2ecf20Sopenharmony_cistatic int tiocmset(struct tty_struct *tty,
31228c2ecf20Sopenharmony_ci		    unsigned int set, unsigned int clear)
31238c2ecf20Sopenharmony_ci{
31248c2ecf20Sopenharmony_ci	struct slgt_info *info = tty->driver_data;
31258c2ecf20Sopenharmony_ci 	unsigned long flags;
31268c2ecf20Sopenharmony_ci
31278c2ecf20Sopenharmony_ci	DBGINFO(("%s tiocmset(%x,%x)\n", info->device_name, set, clear));
31288c2ecf20Sopenharmony_ci
31298c2ecf20Sopenharmony_ci	if (set & TIOCM_RTS)
31308c2ecf20Sopenharmony_ci		info->signals |= SerialSignal_RTS;
31318c2ecf20Sopenharmony_ci	if (set & TIOCM_DTR)
31328c2ecf20Sopenharmony_ci		info->signals |= SerialSignal_DTR;
31338c2ecf20Sopenharmony_ci	if (clear & TIOCM_RTS)
31348c2ecf20Sopenharmony_ci		info->signals &= ~SerialSignal_RTS;
31358c2ecf20Sopenharmony_ci	if (clear & TIOCM_DTR)
31368c2ecf20Sopenharmony_ci		info->signals &= ~SerialSignal_DTR;
31378c2ecf20Sopenharmony_ci
31388c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
31398c2ecf20Sopenharmony_ci	set_gtsignals(info);
31408c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
31418c2ecf20Sopenharmony_ci	return 0;
31428c2ecf20Sopenharmony_ci}
31438c2ecf20Sopenharmony_ci
31448c2ecf20Sopenharmony_cistatic int carrier_raised(struct tty_port *port)
31458c2ecf20Sopenharmony_ci{
31468c2ecf20Sopenharmony_ci	unsigned long flags;
31478c2ecf20Sopenharmony_ci	struct slgt_info *info = container_of(port, struct slgt_info, port);
31488c2ecf20Sopenharmony_ci
31498c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
31508c2ecf20Sopenharmony_ci	get_gtsignals(info);
31518c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
31528c2ecf20Sopenharmony_ci	return (info->signals & SerialSignal_DCD) ? 1 : 0;
31538c2ecf20Sopenharmony_ci}
31548c2ecf20Sopenharmony_ci
31558c2ecf20Sopenharmony_cistatic void dtr_rts(struct tty_port *port, int on)
31568c2ecf20Sopenharmony_ci{
31578c2ecf20Sopenharmony_ci	unsigned long flags;
31588c2ecf20Sopenharmony_ci	struct slgt_info *info = container_of(port, struct slgt_info, port);
31598c2ecf20Sopenharmony_ci
31608c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
31618c2ecf20Sopenharmony_ci	if (on)
31628c2ecf20Sopenharmony_ci		info->signals |= SerialSignal_RTS | SerialSignal_DTR;
31638c2ecf20Sopenharmony_ci	else
31648c2ecf20Sopenharmony_ci		info->signals &= ~(SerialSignal_RTS | SerialSignal_DTR);
31658c2ecf20Sopenharmony_ci	set_gtsignals(info);
31668c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
31678c2ecf20Sopenharmony_ci}
31688c2ecf20Sopenharmony_ci
31698c2ecf20Sopenharmony_ci
31708c2ecf20Sopenharmony_ci/*
31718c2ecf20Sopenharmony_ci *  block current process until the device is ready to open
31728c2ecf20Sopenharmony_ci */
31738c2ecf20Sopenharmony_cistatic int block_til_ready(struct tty_struct *tty, struct file *filp,
31748c2ecf20Sopenharmony_ci			   struct slgt_info *info)
31758c2ecf20Sopenharmony_ci{
31768c2ecf20Sopenharmony_ci	DECLARE_WAITQUEUE(wait, current);
31778c2ecf20Sopenharmony_ci	int		retval;
31788c2ecf20Sopenharmony_ci	bool		do_clocal = false;
31798c2ecf20Sopenharmony_ci	unsigned long	flags;
31808c2ecf20Sopenharmony_ci	int		cd;
31818c2ecf20Sopenharmony_ci	struct tty_port *port = &info->port;
31828c2ecf20Sopenharmony_ci
31838c2ecf20Sopenharmony_ci	DBGINFO(("%s block_til_ready\n", tty->driver->name));
31848c2ecf20Sopenharmony_ci
31858c2ecf20Sopenharmony_ci	if (filp->f_flags & O_NONBLOCK || tty_io_error(tty)) {
31868c2ecf20Sopenharmony_ci		/* nonblock mode is set or port is not enabled */
31878c2ecf20Sopenharmony_ci		tty_port_set_active(port, 1);
31888c2ecf20Sopenharmony_ci		return 0;
31898c2ecf20Sopenharmony_ci	}
31908c2ecf20Sopenharmony_ci
31918c2ecf20Sopenharmony_ci	if (C_CLOCAL(tty))
31928c2ecf20Sopenharmony_ci		do_clocal = true;
31938c2ecf20Sopenharmony_ci
31948c2ecf20Sopenharmony_ci	/* Wait for carrier detect and the line to become
31958c2ecf20Sopenharmony_ci	 * free (i.e., not in use by the callout).  While we are in
31968c2ecf20Sopenharmony_ci	 * this loop, port->count is dropped by one, so that
31978c2ecf20Sopenharmony_ci	 * close() knows when to free things.  We restore it upon
31988c2ecf20Sopenharmony_ci	 * exit, either normal or abnormal.
31998c2ecf20Sopenharmony_ci	 */
32008c2ecf20Sopenharmony_ci
32018c2ecf20Sopenharmony_ci	retval = 0;
32028c2ecf20Sopenharmony_ci	add_wait_queue(&port->open_wait, &wait);
32038c2ecf20Sopenharmony_ci
32048c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->lock, flags);
32058c2ecf20Sopenharmony_ci	port->count--;
32068c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->lock, flags);
32078c2ecf20Sopenharmony_ci	port->blocked_open++;
32088c2ecf20Sopenharmony_ci
32098c2ecf20Sopenharmony_ci	while (1) {
32108c2ecf20Sopenharmony_ci		if (C_BAUD(tty) && tty_port_initialized(port))
32118c2ecf20Sopenharmony_ci			tty_port_raise_dtr_rts(port);
32128c2ecf20Sopenharmony_ci
32138c2ecf20Sopenharmony_ci		set_current_state(TASK_INTERRUPTIBLE);
32148c2ecf20Sopenharmony_ci
32158c2ecf20Sopenharmony_ci		if (tty_hung_up_p(filp) || !tty_port_initialized(port)) {
32168c2ecf20Sopenharmony_ci			retval = (port->flags & ASYNC_HUP_NOTIFY) ?
32178c2ecf20Sopenharmony_ci					-EAGAIN : -ERESTARTSYS;
32188c2ecf20Sopenharmony_ci			break;
32198c2ecf20Sopenharmony_ci		}
32208c2ecf20Sopenharmony_ci
32218c2ecf20Sopenharmony_ci		cd = tty_port_carrier_raised(port);
32228c2ecf20Sopenharmony_ci		if (do_clocal || cd)
32238c2ecf20Sopenharmony_ci			break;
32248c2ecf20Sopenharmony_ci
32258c2ecf20Sopenharmony_ci		if (signal_pending(current)) {
32268c2ecf20Sopenharmony_ci			retval = -ERESTARTSYS;
32278c2ecf20Sopenharmony_ci			break;
32288c2ecf20Sopenharmony_ci		}
32298c2ecf20Sopenharmony_ci
32308c2ecf20Sopenharmony_ci		DBGINFO(("%s block_til_ready wait\n", tty->driver->name));
32318c2ecf20Sopenharmony_ci		tty_unlock(tty);
32328c2ecf20Sopenharmony_ci		schedule();
32338c2ecf20Sopenharmony_ci		tty_lock(tty);
32348c2ecf20Sopenharmony_ci	}
32358c2ecf20Sopenharmony_ci
32368c2ecf20Sopenharmony_ci	set_current_state(TASK_RUNNING);
32378c2ecf20Sopenharmony_ci	remove_wait_queue(&port->open_wait, &wait);
32388c2ecf20Sopenharmony_ci
32398c2ecf20Sopenharmony_ci	if (!tty_hung_up_p(filp))
32408c2ecf20Sopenharmony_ci		port->count++;
32418c2ecf20Sopenharmony_ci	port->blocked_open--;
32428c2ecf20Sopenharmony_ci
32438c2ecf20Sopenharmony_ci	if (!retval)
32448c2ecf20Sopenharmony_ci		tty_port_set_active(port, 1);
32458c2ecf20Sopenharmony_ci
32468c2ecf20Sopenharmony_ci	DBGINFO(("%s block_til_ready ready, rc=%d\n", tty->driver->name, retval));
32478c2ecf20Sopenharmony_ci	return retval;
32488c2ecf20Sopenharmony_ci}
32498c2ecf20Sopenharmony_ci
32508c2ecf20Sopenharmony_ci/*
32518c2ecf20Sopenharmony_ci * allocate buffers used for calling line discipline receive_buf
32528c2ecf20Sopenharmony_ci * directly in synchronous mode
32538c2ecf20Sopenharmony_ci * note: add 5 bytes to max frame size to allow appending
32548c2ecf20Sopenharmony_ci * 32-bit CRC and status byte when configured to do so
32558c2ecf20Sopenharmony_ci */
32568c2ecf20Sopenharmony_cistatic int alloc_tmp_rbuf(struct slgt_info *info)
32578c2ecf20Sopenharmony_ci{
32588c2ecf20Sopenharmony_ci	info->tmp_rbuf = kmalloc(info->max_frame_size + 5, GFP_KERNEL);
32598c2ecf20Sopenharmony_ci	if (info->tmp_rbuf == NULL)
32608c2ecf20Sopenharmony_ci		return -ENOMEM;
32618c2ecf20Sopenharmony_ci	/* unused flag buffer to satisfy receive_buf calling interface */
32628c2ecf20Sopenharmony_ci	info->flag_buf = kzalloc(info->max_frame_size + 5, GFP_KERNEL);
32638c2ecf20Sopenharmony_ci	if (!info->flag_buf) {
32648c2ecf20Sopenharmony_ci		kfree(info->tmp_rbuf);
32658c2ecf20Sopenharmony_ci		info->tmp_rbuf = NULL;
32668c2ecf20Sopenharmony_ci		return -ENOMEM;
32678c2ecf20Sopenharmony_ci	}
32688c2ecf20Sopenharmony_ci	return 0;
32698c2ecf20Sopenharmony_ci}
32708c2ecf20Sopenharmony_ci
32718c2ecf20Sopenharmony_cistatic void free_tmp_rbuf(struct slgt_info *info)
32728c2ecf20Sopenharmony_ci{
32738c2ecf20Sopenharmony_ci	kfree(info->tmp_rbuf);
32748c2ecf20Sopenharmony_ci	info->tmp_rbuf = NULL;
32758c2ecf20Sopenharmony_ci	kfree(info->flag_buf);
32768c2ecf20Sopenharmony_ci	info->flag_buf = NULL;
32778c2ecf20Sopenharmony_ci}
32788c2ecf20Sopenharmony_ci
32798c2ecf20Sopenharmony_ci/*
32808c2ecf20Sopenharmony_ci * allocate DMA descriptor lists.
32818c2ecf20Sopenharmony_ci */
32828c2ecf20Sopenharmony_cistatic int alloc_desc(struct slgt_info *info)
32838c2ecf20Sopenharmony_ci{
32848c2ecf20Sopenharmony_ci	unsigned int i;
32858c2ecf20Sopenharmony_ci	unsigned int pbufs;
32868c2ecf20Sopenharmony_ci
32878c2ecf20Sopenharmony_ci	/* allocate memory to hold descriptor lists */
32888c2ecf20Sopenharmony_ci	info->bufs = dma_alloc_coherent(&info->pdev->dev, DESC_LIST_SIZE,
32898c2ecf20Sopenharmony_ci					&info->bufs_dma_addr, GFP_KERNEL);
32908c2ecf20Sopenharmony_ci	if (info->bufs == NULL)
32918c2ecf20Sopenharmony_ci		return -ENOMEM;
32928c2ecf20Sopenharmony_ci
32938c2ecf20Sopenharmony_ci	info->rbufs = (struct slgt_desc*)info->bufs;
32948c2ecf20Sopenharmony_ci	info->tbufs = ((struct slgt_desc*)info->bufs) + info->rbuf_count;
32958c2ecf20Sopenharmony_ci
32968c2ecf20Sopenharmony_ci	pbufs = (unsigned int)info->bufs_dma_addr;
32978c2ecf20Sopenharmony_ci
32988c2ecf20Sopenharmony_ci	/*
32998c2ecf20Sopenharmony_ci	 * Build circular lists of descriptors
33008c2ecf20Sopenharmony_ci	 */
33018c2ecf20Sopenharmony_ci
33028c2ecf20Sopenharmony_ci	for (i=0; i < info->rbuf_count; i++) {
33038c2ecf20Sopenharmony_ci		/* physical address of this descriptor */
33048c2ecf20Sopenharmony_ci		info->rbufs[i].pdesc = pbufs + (i * sizeof(struct slgt_desc));
33058c2ecf20Sopenharmony_ci
33068c2ecf20Sopenharmony_ci		/* physical address of next descriptor */
33078c2ecf20Sopenharmony_ci		if (i == info->rbuf_count - 1)
33088c2ecf20Sopenharmony_ci			info->rbufs[i].next = cpu_to_le32(pbufs);
33098c2ecf20Sopenharmony_ci		else
33108c2ecf20Sopenharmony_ci			info->rbufs[i].next = cpu_to_le32(pbufs + ((i+1) * sizeof(struct slgt_desc)));
33118c2ecf20Sopenharmony_ci		set_desc_count(info->rbufs[i], DMABUFSIZE);
33128c2ecf20Sopenharmony_ci	}
33138c2ecf20Sopenharmony_ci
33148c2ecf20Sopenharmony_ci	for (i=0; i < info->tbuf_count; i++) {
33158c2ecf20Sopenharmony_ci		/* physical address of this descriptor */
33168c2ecf20Sopenharmony_ci		info->tbufs[i].pdesc = pbufs + ((info->rbuf_count + i) * sizeof(struct slgt_desc));
33178c2ecf20Sopenharmony_ci
33188c2ecf20Sopenharmony_ci		/* physical address of next descriptor */
33198c2ecf20Sopenharmony_ci		if (i == info->tbuf_count - 1)
33208c2ecf20Sopenharmony_ci			info->tbufs[i].next = cpu_to_le32(pbufs + info->rbuf_count * sizeof(struct slgt_desc));
33218c2ecf20Sopenharmony_ci		else
33228c2ecf20Sopenharmony_ci			info->tbufs[i].next = cpu_to_le32(pbufs + ((info->rbuf_count + i + 1) * sizeof(struct slgt_desc)));
33238c2ecf20Sopenharmony_ci	}
33248c2ecf20Sopenharmony_ci
33258c2ecf20Sopenharmony_ci	return 0;
33268c2ecf20Sopenharmony_ci}
33278c2ecf20Sopenharmony_ci
33288c2ecf20Sopenharmony_cistatic void free_desc(struct slgt_info *info)
33298c2ecf20Sopenharmony_ci{
33308c2ecf20Sopenharmony_ci	if (info->bufs != NULL) {
33318c2ecf20Sopenharmony_ci		dma_free_coherent(&info->pdev->dev, DESC_LIST_SIZE,
33328c2ecf20Sopenharmony_ci				  info->bufs, info->bufs_dma_addr);
33338c2ecf20Sopenharmony_ci		info->bufs  = NULL;
33348c2ecf20Sopenharmony_ci		info->rbufs = NULL;
33358c2ecf20Sopenharmony_ci		info->tbufs = NULL;
33368c2ecf20Sopenharmony_ci	}
33378c2ecf20Sopenharmony_ci}
33388c2ecf20Sopenharmony_ci
33398c2ecf20Sopenharmony_cistatic int alloc_bufs(struct slgt_info *info, struct slgt_desc *bufs, int count)
33408c2ecf20Sopenharmony_ci{
33418c2ecf20Sopenharmony_ci	int i;
33428c2ecf20Sopenharmony_ci	for (i=0; i < count; i++) {
33438c2ecf20Sopenharmony_ci		bufs[i].buf = dma_alloc_coherent(&info->pdev->dev, DMABUFSIZE,
33448c2ecf20Sopenharmony_ci						 &bufs[i].buf_dma_addr, GFP_KERNEL);
33458c2ecf20Sopenharmony_ci		if (!bufs[i].buf)
33468c2ecf20Sopenharmony_ci			return -ENOMEM;
33478c2ecf20Sopenharmony_ci		bufs[i].pbuf  = cpu_to_le32((unsigned int)bufs[i].buf_dma_addr);
33488c2ecf20Sopenharmony_ci	}
33498c2ecf20Sopenharmony_ci	return 0;
33508c2ecf20Sopenharmony_ci}
33518c2ecf20Sopenharmony_ci
33528c2ecf20Sopenharmony_cistatic void free_bufs(struct slgt_info *info, struct slgt_desc *bufs, int count)
33538c2ecf20Sopenharmony_ci{
33548c2ecf20Sopenharmony_ci	int i;
33558c2ecf20Sopenharmony_ci	for (i=0; i < count; i++) {
33568c2ecf20Sopenharmony_ci		if (bufs[i].buf == NULL)
33578c2ecf20Sopenharmony_ci			continue;
33588c2ecf20Sopenharmony_ci		dma_free_coherent(&info->pdev->dev, DMABUFSIZE, bufs[i].buf,
33598c2ecf20Sopenharmony_ci				  bufs[i].buf_dma_addr);
33608c2ecf20Sopenharmony_ci		bufs[i].buf = NULL;
33618c2ecf20Sopenharmony_ci	}
33628c2ecf20Sopenharmony_ci}
33638c2ecf20Sopenharmony_ci
33648c2ecf20Sopenharmony_cistatic int alloc_dma_bufs(struct slgt_info *info)
33658c2ecf20Sopenharmony_ci{
33668c2ecf20Sopenharmony_ci	info->rbuf_count = 32;
33678c2ecf20Sopenharmony_ci	info->tbuf_count = 32;
33688c2ecf20Sopenharmony_ci
33698c2ecf20Sopenharmony_ci	if (alloc_desc(info) < 0 ||
33708c2ecf20Sopenharmony_ci	    alloc_bufs(info, info->rbufs, info->rbuf_count) < 0 ||
33718c2ecf20Sopenharmony_ci	    alloc_bufs(info, info->tbufs, info->tbuf_count) < 0 ||
33728c2ecf20Sopenharmony_ci	    alloc_tmp_rbuf(info) < 0) {
33738c2ecf20Sopenharmony_ci		DBGERR(("%s DMA buffer alloc fail\n", info->device_name));
33748c2ecf20Sopenharmony_ci		return -ENOMEM;
33758c2ecf20Sopenharmony_ci	}
33768c2ecf20Sopenharmony_ci	reset_rbufs(info);
33778c2ecf20Sopenharmony_ci	return 0;
33788c2ecf20Sopenharmony_ci}
33798c2ecf20Sopenharmony_ci
33808c2ecf20Sopenharmony_cistatic void free_dma_bufs(struct slgt_info *info)
33818c2ecf20Sopenharmony_ci{
33828c2ecf20Sopenharmony_ci	if (info->bufs) {
33838c2ecf20Sopenharmony_ci		free_bufs(info, info->rbufs, info->rbuf_count);
33848c2ecf20Sopenharmony_ci		free_bufs(info, info->tbufs, info->tbuf_count);
33858c2ecf20Sopenharmony_ci		free_desc(info);
33868c2ecf20Sopenharmony_ci	}
33878c2ecf20Sopenharmony_ci	free_tmp_rbuf(info);
33888c2ecf20Sopenharmony_ci}
33898c2ecf20Sopenharmony_ci
33908c2ecf20Sopenharmony_cistatic int claim_resources(struct slgt_info *info)
33918c2ecf20Sopenharmony_ci{
33928c2ecf20Sopenharmony_ci	if (request_mem_region(info->phys_reg_addr, SLGT_REG_SIZE, "synclink_gt") == NULL) {
33938c2ecf20Sopenharmony_ci		DBGERR(("%s reg addr conflict, addr=%08X\n",
33948c2ecf20Sopenharmony_ci			info->device_name, info->phys_reg_addr));
33958c2ecf20Sopenharmony_ci		info->init_error = DiagStatus_AddressConflict;
33968c2ecf20Sopenharmony_ci		goto errout;
33978c2ecf20Sopenharmony_ci	}
33988c2ecf20Sopenharmony_ci	else
33998c2ecf20Sopenharmony_ci		info->reg_addr_requested = true;
34008c2ecf20Sopenharmony_ci
34018c2ecf20Sopenharmony_ci	info->reg_addr = ioremap(info->phys_reg_addr, SLGT_REG_SIZE);
34028c2ecf20Sopenharmony_ci	if (!info->reg_addr) {
34038c2ecf20Sopenharmony_ci		DBGERR(("%s can't map device registers, addr=%08X\n",
34048c2ecf20Sopenharmony_ci			info->device_name, info->phys_reg_addr));
34058c2ecf20Sopenharmony_ci		info->init_error = DiagStatus_CantAssignPciResources;
34068c2ecf20Sopenharmony_ci		goto errout;
34078c2ecf20Sopenharmony_ci	}
34088c2ecf20Sopenharmony_ci	return 0;
34098c2ecf20Sopenharmony_ci
34108c2ecf20Sopenharmony_cierrout:
34118c2ecf20Sopenharmony_ci	release_resources(info);
34128c2ecf20Sopenharmony_ci	return -ENODEV;
34138c2ecf20Sopenharmony_ci}
34148c2ecf20Sopenharmony_ci
34158c2ecf20Sopenharmony_cistatic void release_resources(struct slgt_info *info)
34168c2ecf20Sopenharmony_ci{
34178c2ecf20Sopenharmony_ci	if (info->irq_requested) {
34188c2ecf20Sopenharmony_ci		free_irq(info->irq_level, info);
34198c2ecf20Sopenharmony_ci		info->irq_requested = false;
34208c2ecf20Sopenharmony_ci	}
34218c2ecf20Sopenharmony_ci
34228c2ecf20Sopenharmony_ci	if (info->reg_addr_requested) {
34238c2ecf20Sopenharmony_ci		release_mem_region(info->phys_reg_addr, SLGT_REG_SIZE);
34248c2ecf20Sopenharmony_ci		info->reg_addr_requested = false;
34258c2ecf20Sopenharmony_ci	}
34268c2ecf20Sopenharmony_ci
34278c2ecf20Sopenharmony_ci	if (info->reg_addr) {
34288c2ecf20Sopenharmony_ci		iounmap(info->reg_addr);
34298c2ecf20Sopenharmony_ci		info->reg_addr = NULL;
34308c2ecf20Sopenharmony_ci	}
34318c2ecf20Sopenharmony_ci}
34328c2ecf20Sopenharmony_ci
34338c2ecf20Sopenharmony_ci/* Add the specified device instance data structure to the
34348c2ecf20Sopenharmony_ci * global linked list of devices and increment the device count.
34358c2ecf20Sopenharmony_ci */
34368c2ecf20Sopenharmony_cistatic void add_device(struct slgt_info *info)
34378c2ecf20Sopenharmony_ci{
34388c2ecf20Sopenharmony_ci	char *devstr;
34398c2ecf20Sopenharmony_ci
34408c2ecf20Sopenharmony_ci	info->next_device = NULL;
34418c2ecf20Sopenharmony_ci	info->line = slgt_device_count;
34428c2ecf20Sopenharmony_ci	sprintf(info->device_name, "%s%d", tty_dev_prefix, info->line);
34438c2ecf20Sopenharmony_ci
34448c2ecf20Sopenharmony_ci	if (info->line < MAX_DEVICES) {
34458c2ecf20Sopenharmony_ci		if (maxframe[info->line])
34468c2ecf20Sopenharmony_ci			info->max_frame_size = maxframe[info->line];
34478c2ecf20Sopenharmony_ci	}
34488c2ecf20Sopenharmony_ci
34498c2ecf20Sopenharmony_ci	slgt_device_count++;
34508c2ecf20Sopenharmony_ci
34518c2ecf20Sopenharmony_ci	if (!slgt_device_list)
34528c2ecf20Sopenharmony_ci		slgt_device_list = info;
34538c2ecf20Sopenharmony_ci	else {
34548c2ecf20Sopenharmony_ci		struct slgt_info *current_dev = slgt_device_list;
34558c2ecf20Sopenharmony_ci		while(current_dev->next_device)
34568c2ecf20Sopenharmony_ci			current_dev = current_dev->next_device;
34578c2ecf20Sopenharmony_ci		current_dev->next_device = info;
34588c2ecf20Sopenharmony_ci	}
34598c2ecf20Sopenharmony_ci
34608c2ecf20Sopenharmony_ci	if (info->max_frame_size < 4096)
34618c2ecf20Sopenharmony_ci		info->max_frame_size = 4096;
34628c2ecf20Sopenharmony_ci	else if (info->max_frame_size > 65535)
34638c2ecf20Sopenharmony_ci		info->max_frame_size = 65535;
34648c2ecf20Sopenharmony_ci
34658c2ecf20Sopenharmony_ci	switch(info->pdev->device) {
34668c2ecf20Sopenharmony_ci	case SYNCLINK_GT_DEVICE_ID:
34678c2ecf20Sopenharmony_ci		devstr = "GT";
34688c2ecf20Sopenharmony_ci		break;
34698c2ecf20Sopenharmony_ci	case SYNCLINK_GT2_DEVICE_ID:
34708c2ecf20Sopenharmony_ci		devstr = "GT2";
34718c2ecf20Sopenharmony_ci		break;
34728c2ecf20Sopenharmony_ci	case SYNCLINK_GT4_DEVICE_ID:
34738c2ecf20Sopenharmony_ci		devstr = "GT4";
34748c2ecf20Sopenharmony_ci		break;
34758c2ecf20Sopenharmony_ci	case SYNCLINK_AC_DEVICE_ID:
34768c2ecf20Sopenharmony_ci		devstr = "AC";
34778c2ecf20Sopenharmony_ci		info->params.mode = MGSL_MODE_ASYNC;
34788c2ecf20Sopenharmony_ci		break;
34798c2ecf20Sopenharmony_ci	default:
34808c2ecf20Sopenharmony_ci		devstr = "(unknown model)";
34818c2ecf20Sopenharmony_ci	}
34828c2ecf20Sopenharmony_ci	printk("SyncLink %s %s IO=%08x IRQ=%d MaxFrameSize=%u\n",
34838c2ecf20Sopenharmony_ci		devstr, info->device_name, info->phys_reg_addr,
34848c2ecf20Sopenharmony_ci		info->irq_level, info->max_frame_size);
34858c2ecf20Sopenharmony_ci
34868c2ecf20Sopenharmony_ci#if SYNCLINK_GENERIC_HDLC
34878c2ecf20Sopenharmony_ci	hdlcdev_init(info);
34888c2ecf20Sopenharmony_ci#endif
34898c2ecf20Sopenharmony_ci}
34908c2ecf20Sopenharmony_ci
34918c2ecf20Sopenharmony_cistatic const struct tty_port_operations slgt_port_ops = {
34928c2ecf20Sopenharmony_ci	.carrier_raised = carrier_raised,
34938c2ecf20Sopenharmony_ci	.dtr_rts = dtr_rts,
34948c2ecf20Sopenharmony_ci};
34958c2ecf20Sopenharmony_ci
34968c2ecf20Sopenharmony_ci/*
34978c2ecf20Sopenharmony_ci *  allocate device instance structure, return NULL on failure
34988c2ecf20Sopenharmony_ci */
34998c2ecf20Sopenharmony_cistatic struct slgt_info *alloc_dev(int adapter_num, int port_num, struct pci_dev *pdev)
35008c2ecf20Sopenharmony_ci{
35018c2ecf20Sopenharmony_ci	struct slgt_info *info;
35028c2ecf20Sopenharmony_ci
35038c2ecf20Sopenharmony_ci	info = kzalloc(sizeof(struct slgt_info), GFP_KERNEL);
35048c2ecf20Sopenharmony_ci
35058c2ecf20Sopenharmony_ci	if (!info) {
35068c2ecf20Sopenharmony_ci		DBGERR(("%s device alloc failed adapter=%d port=%d\n",
35078c2ecf20Sopenharmony_ci			driver_name, adapter_num, port_num));
35088c2ecf20Sopenharmony_ci	} else {
35098c2ecf20Sopenharmony_ci		tty_port_init(&info->port);
35108c2ecf20Sopenharmony_ci		info->port.ops = &slgt_port_ops;
35118c2ecf20Sopenharmony_ci		info->magic = MGSL_MAGIC;
35128c2ecf20Sopenharmony_ci		INIT_WORK(&info->task, bh_handler);
35138c2ecf20Sopenharmony_ci		info->max_frame_size = 4096;
35148c2ecf20Sopenharmony_ci		info->base_clock = 14745600;
35158c2ecf20Sopenharmony_ci		info->rbuf_fill_level = DMABUFSIZE;
35168c2ecf20Sopenharmony_ci		info->port.close_delay = 5*HZ/10;
35178c2ecf20Sopenharmony_ci		info->port.closing_wait = 30*HZ;
35188c2ecf20Sopenharmony_ci		init_waitqueue_head(&info->status_event_wait_q);
35198c2ecf20Sopenharmony_ci		init_waitqueue_head(&info->event_wait_q);
35208c2ecf20Sopenharmony_ci		spin_lock_init(&info->netlock);
35218c2ecf20Sopenharmony_ci		memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS));
35228c2ecf20Sopenharmony_ci		info->idle_mode = HDLC_TXIDLE_FLAGS;
35238c2ecf20Sopenharmony_ci		info->adapter_num = adapter_num;
35248c2ecf20Sopenharmony_ci		info->port_num = port_num;
35258c2ecf20Sopenharmony_ci
35268c2ecf20Sopenharmony_ci		timer_setup(&info->tx_timer, tx_timeout, 0);
35278c2ecf20Sopenharmony_ci		timer_setup(&info->rx_timer, rx_timeout, 0);
35288c2ecf20Sopenharmony_ci
35298c2ecf20Sopenharmony_ci		/* Copy configuration info to device instance data */
35308c2ecf20Sopenharmony_ci		info->pdev = pdev;
35318c2ecf20Sopenharmony_ci		info->irq_level = pdev->irq;
35328c2ecf20Sopenharmony_ci		info->phys_reg_addr = pci_resource_start(pdev,0);
35338c2ecf20Sopenharmony_ci
35348c2ecf20Sopenharmony_ci		info->bus_type = MGSL_BUS_TYPE_PCI;
35358c2ecf20Sopenharmony_ci		info->irq_flags = IRQF_SHARED;
35368c2ecf20Sopenharmony_ci
35378c2ecf20Sopenharmony_ci		info->init_error = -1; /* assume error, set to 0 on successful init */
35388c2ecf20Sopenharmony_ci	}
35398c2ecf20Sopenharmony_ci
35408c2ecf20Sopenharmony_ci	return info;
35418c2ecf20Sopenharmony_ci}
35428c2ecf20Sopenharmony_ci
35438c2ecf20Sopenharmony_cistatic void device_init(int adapter_num, struct pci_dev *pdev)
35448c2ecf20Sopenharmony_ci{
35458c2ecf20Sopenharmony_ci	struct slgt_info *port_array[SLGT_MAX_PORTS];
35468c2ecf20Sopenharmony_ci	int i;
35478c2ecf20Sopenharmony_ci	int port_count = 1;
35488c2ecf20Sopenharmony_ci
35498c2ecf20Sopenharmony_ci	if (pdev->device == SYNCLINK_GT2_DEVICE_ID)
35508c2ecf20Sopenharmony_ci		port_count = 2;
35518c2ecf20Sopenharmony_ci	else if (pdev->device == SYNCLINK_GT4_DEVICE_ID)
35528c2ecf20Sopenharmony_ci		port_count = 4;
35538c2ecf20Sopenharmony_ci
35548c2ecf20Sopenharmony_ci	/* allocate device instances for all ports */
35558c2ecf20Sopenharmony_ci	for (i=0; i < port_count; ++i) {
35568c2ecf20Sopenharmony_ci		port_array[i] = alloc_dev(adapter_num, i, pdev);
35578c2ecf20Sopenharmony_ci		if (port_array[i] == NULL) {
35588c2ecf20Sopenharmony_ci			for (--i; i >= 0; --i) {
35598c2ecf20Sopenharmony_ci				tty_port_destroy(&port_array[i]->port);
35608c2ecf20Sopenharmony_ci				kfree(port_array[i]);
35618c2ecf20Sopenharmony_ci			}
35628c2ecf20Sopenharmony_ci			return;
35638c2ecf20Sopenharmony_ci		}
35648c2ecf20Sopenharmony_ci	}
35658c2ecf20Sopenharmony_ci
35668c2ecf20Sopenharmony_ci	/* give copy of port_array to all ports and add to device list  */
35678c2ecf20Sopenharmony_ci	for (i=0; i < port_count; ++i) {
35688c2ecf20Sopenharmony_ci		memcpy(port_array[i]->port_array, port_array, sizeof(port_array));
35698c2ecf20Sopenharmony_ci		add_device(port_array[i]);
35708c2ecf20Sopenharmony_ci		port_array[i]->port_count = port_count;
35718c2ecf20Sopenharmony_ci		spin_lock_init(&port_array[i]->lock);
35728c2ecf20Sopenharmony_ci	}
35738c2ecf20Sopenharmony_ci
35748c2ecf20Sopenharmony_ci	/* Allocate and claim adapter resources */
35758c2ecf20Sopenharmony_ci	if (!claim_resources(port_array[0])) {
35768c2ecf20Sopenharmony_ci
35778c2ecf20Sopenharmony_ci		alloc_dma_bufs(port_array[0]);
35788c2ecf20Sopenharmony_ci
35798c2ecf20Sopenharmony_ci		/* copy resource information from first port to others */
35808c2ecf20Sopenharmony_ci		for (i = 1; i < port_count; ++i) {
35818c2ecf20Sopenharmony_ci			port_array[i]->irq_level = port_array[0]->irq_level;
35828c2ecf20Sopenharmony_ci			port_array[i]->reg_addr  = port_array[0]->reg_addr;
35838c2ecf20Sopenharmony_ci			alloc_dma_bufs(port_array[i]);
35848c2ecf20Sopenharmony_ci		}
35858c2ecf20Sopenharmony_ci
35868c2ecf20Sopenharmony_ci		if (request_irq(port_array[0]->irq_level,
35878c2ecf20Sopenharmony_ci					slgt_interrupt,
35888c2ecf20Sopenharmony_ci					port_array[0]->irq_flags,
35898c2ecf20Sopenharmony_ci					port_array[0]->device_name,
35908c2ecf20Sopenharmony_ci					port_array[0]) < 0) {
35918c2ecf20Sopenharmony_ci			DBGERR(("%s request_irq failed IRQ=%d\n",
35928c2ecf20Sopenharmony_ci				port_array[0]->device_name,
35938c2ecf20Sopenharmony_ci				port_array[0]->irq_level));
35948c2ecf20Sopenharmony_ci		} else {
35958c2ecf20Sopenharmony_ci			port_array[0]->irq_requested = true;
35968c2ecf20Sopenharmony_ci			adapter_test(port_array[0]);
35978c2ecf20Sopenharmony_ci			for (i=1 ; i < port_count ; i++) {
35988c2ecf20Sopenharmony_ci				port_array[i]->init_error = port_array[0]->init_error;
35998c2ecf20Sopenharmony_ci				port_array[i]->gpio_present = port_array[0]->gpio_present;
36008c2ecf20Sopenharmony_ci			}
36018c2ecf20Sopenharmony_ci		}
36028c2ecf20Sopenharmony_ci	}
36038c2ecf20Sopenharmony_ci
36048c2ecf20Sopenharmony_ci	for (i = 0; i < port_count; ++i) {
36058c2ecf20Sopenharmony_ci		struct slgt_info *info = port_array[i];
36068c2ecf20Sopenharmony_ci		tty_port_register_device(&info->port, serial_driver, info->line,
36078c2ecf20Sopenharmony_ci				&info->pdev->dev);
36088c2ecf20Sopenharmony_ci	}
36098c2ecf20Sopenharmony_ci}
36108c2ecf20Sopenharmony_ci
36118c2ecf20Sopenharmony_cistatic int init_one(struct pci_dev *dev,
36128c2ecf20Sopenharmony_ci			      const struct pci_device_id *ent)
36138c2ecf20Sopenharmony_ci{
36148c2ecf20Sopenharmony_ci	if (pci_enable_device(dev)) {
36158c2ecf20Sopenharmony_ci		printk("error enabling pci device %p\n", dev);
36168c2ecf20Sopenharmony_ci		return -EIO;
36178c2ecf20Sopenharmony_ci	}
36188c2ecf20Sopenharmony_ci	pci_set_master(dev);
36198c2ecf20Sopenharmony_ci	device_init(slgt_device_count, dev);
36208c2ecf20Sopenharmony_ci	return 0;
36218c2ecf20Sopenharmony_ci}
36228c2ecf20Sopenharmony_ci
36238c2ecf20Sopenharmony_cistatic void remove_one(struct pci_dev *dev)
36248c2ecf20Sopenharmony_ci{
36258c2ecf20Sopenharmony_ci}
36268c2ecf20Sopenharmony_ci
36278c2ecf20Sopenharmony_cistatic const struct tty_operations ops = {
36288c2ecf20Sopenharmony_ci	.open = open,
36298c2ecf20Sopenharmony_ci	.close = close,
36308c2ecf20Sopenharmony_ci	.write = write,
36318c2ecf20Sopenharmony_ci	.put_char = put_char,
36328c2ecf20Sopenharmony_ci	.flush_chars = flush_chars,
36338c2ecf20Sopenharmony_ci	.write_room = write_room,
36348c2ecf20Sopenharmony_ci	.chars_in_buffer = chars_in_buffer,
36358c2ecf20Sopenharmony_ci	.flush_buffer = flush_buffer,
36368c2ecf20Sopenharmony_ci	.ioctl = ioctl,
36378c2ecf20Sopenharmony_ci	.compat_ioctl = slgt_compat_ioctl,
36388c2ecf20Sopenharmony_ci	.throttle = throttle,
36398c2ecf20Sopenharmony_ci	.unthrottle = unthrottle,
36408c2ecf20Sopenharmony_ci	.send_xchar = send_xchar,
36418c2ecf20Sopenharmony_ci	.break_ctl = set_break,
36428c2ecf20Sopenharmony_ci	.wait_until_sent = wait_until_sent,
36438c2ecf20Sopenharmony_ci	.set_termios = set_termios,
36448c2ecf20Sopenharmony_ci	.stop = tx_hold,
36458c2ecf20Sopenharmony_ci	.start = tx_release,
36468c2ecf20Sopenharmony_ci	.hangup = hangup,
36478c2ecf20Sopenharmony_ci	.tiocmget = tiocmget,
36488c2ecf20Sopenharmony_ci	.tiocmset = tiocmset,
36498c2ecf20Sopenharmony_ci	.get_icount = get_icount,
36508c2ecf20Sopenharmony_ci	.proc_show = synclink_gt_proc_show,
36518c2ecf20Sopenharmony_ci};
36528c2ecf20Sopenharmony_ci
36538c2ecf20Sopenharmony_cistatic void slgt_cleanup(void)
36548c2ecf20Sopenharmony_ci{
36558c2ecf20Sopenharmony_ci	int rc;
36568c2ecf20Sopenharmony_ci	struct slgt_info *info;
36578c2ecf20Sopenharmony_ci	struct slgt_info *tmp;
36588c2ecf20Sopenharmony_ci
36598c2ecf20Sopenharmony_ci	printk(KERN_INFO "unload %s\n", driver_name);
36608c2ecf20Sopenharmony_ci
36618c2ecf20Sopenharmony_ci	if (serial_driver) {
36628c2ecf20Sopenharmony_ci		for (info=slgt_device_list ; info != NULL ; info=info->next_device)
36638c2ecf20Sopenharmony_ci			tty_unregister_device(serial_driver, info->line);
36648c2ecf20Sopenharmony_ci		rc = tty_unregister_driver(serial_driver);
36658c2ecf20Sopenharmony_ci		if (rc)
36668c2ecf20Sopenharmony_ci			DBGERR(("tty_unregister_driver error=%d\n", rc));
36678c2ecf20Sopenharmony_ci		put_tty_driver(serial_driver);
36688c2ecf20Sopenharmony_ci	}
36698c2ecf20Sopenharmony_ci
36708c2ecf20Sopenharmony_ci	/* reset devices */
36718c2ecf20Sopenharmony_ci	info = slgt_device_list;
36728c2ecf20Sopenharmony_ci	while(info) {
36738c2ecf20Sopenharmony_ci		reset_port(info);
36748c2ecf20Sopenharmony_ci		info = info->next_device;
36758c2ecf20Sopenharmony_ci	}
36768c2ecf20Sopenharmony_ci
36778c2ecf20Sopenharmony_ci	/* release devices */
36788c2ecf20Sopenharmony_ci	info = slgt_device_list;
36798c2ecf20Sopenharmony_ci	while(info) {
36808c2ecf20Sopenharmony_ci#if SYNCLINK_GENERIC_HDLC
36818c2ecf20Sopenharmony_ci		hdlcdev_exit(info);
36828c2ecf20Sopenharmony_ci#endif
36838c2ecf20Sopenharmony_ci		free_dma_bufs(info);
36848c2ecf20Sopenharmony_ci		free_tmp_rbuf(info);
36858c2ecf20Sopenharmony_ci		if (info->port_num == 0)
36868c2ecf20Sopenharmony_ci			release_resources(info);
36878c2ecf20Sopenharmony_ci		tmp = info;
36888c2ecf20Sopenharmony_ci		info = info->next_device;
36898c2ecf20Sopenharmony_ci		tty_port_destroy(&tmp->port);
36908c2ecf20Sopenharmony_ci		kfree(tmp);
36918c2ecf20Sopenharmony_ci	}
36928c2ecf20Sopenharmony_ci
36938c2ecf20Sopenharmony_ci	if (pci_registered)
36948c2ecf20Sopenharmony_ci		pci_unregister_driver(&pci_driver);
36958c2ecf20Sopenharmony_ci}
36968c2ecf20Sopenharmony_ci
36978c2ecf20Sopenharmony_ci/*
36988c2ecf20Sopenharmony_ci *  Driver initialization entry point.
36998c2ecf20Sopenharmony_ci */
37008c2ecf20Sopenharmony_cistatic int __init slgt_init(void)
37018c2ecf20Sopenharmony_ci{
37028c2ecf20Sopenharmony_ci	int rc;
37038c2ecf20Sopenharmony_ci
37048c2ecf20Sopenharmony_ci	printk(KERN_INFO "%s\n", driver_name);
37058c2ecf20Sopenharmony_ci
37068c2ecf20Sopenharmony_ci	serial_driver = alloc_tty_driver(MAX_DEVICES);
37078c2ecf20Sopenharmony_ci	if (!serial_driver) {
37088c2ecf20Sopenharmony_ci		printk("%s can't allocate tty driver\n", driver_name);
37098c2ecf20Sopenharmony_ci		return -ENOMEM;
37108c2ecf20Sopenharmony_ci	}
37118c2ecf20Sopenharmony_ci
37128c2ecf20Sopenharmony_ci	/* Initialize the tty_driver structure */
37138c2ecf20Sopenharmony_ci
37148c2ecf20Sopenharmony_ci	serial_driver->driver_name = slgt_driver_name;
37158c2ecf20Sopenharmony_ci	serial_driver->name = tty_dev_prefix;
37168c2ecf20Sopenharmony_ci	serial_driver->major = ttymajor;
37178c2ecf20Sopenharmony_ci	serial_driver->minor_start = 64;
37188c2ecf20Sopenharmony_ci	serial_driver->type = TTY_DRIVER_TYPE_SERIAL;
37198c2ecf20Sopenharmony_ci	serial_driver->subtype = SERIAL_TYPE_NORMAL;
37208c2ecf20Sopenharmony_ci	serial_driver->init_termios = tty_std_termios;
37218c2ecf20Sopenharmony_ci	serial_driver->init_termios.c_cflag =
37228c2ecf20Sopenharmony_ci		B9600 | CS8 | CREAD | HUPCL | CLOCAL;
37238c2ecf20Sopenharmony_ci	serial_driver->init_termios.c_ispeed = 9600;
37248c2ecf20Sopenharmony_ci	serial_driver->init_termios.c_ospeed = 9600;
37258c2ecf20Sopenharmony_ci	serial_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
37268c2ecf20Sopenharmony_ci	tty_set_operations(serial_driver, &ops);
37278c2ecf20Sopenharmony_ci	if ((rc = tty_register_driver(serial_driver)) < 0) {
37288c2ecf20Sopenharmony_ci		DBGERR(("%s can't register serial driver\n", driver_name));
37298c2ecf20Sopenharmony_ci		put_tty_driver(serial_driver);
37308c2ecf20Sopenharmony_ci		serial_driver = NULL;
37318c2ecf20Sopenharmony_ci		goto error;
37328c2ecf20Sopenharmony_ci	}
37338c2ecf20Sopenharmony_ci
37348c2ecf20Sopenharmony_ci	printk(KERN_INFO "%s, tty major#%d\n",
37358c2ecf20Sopenharmony_ci	       driver_name, serial_driver->major);
37368c2ecf20Sopenharmony_ci
37378c2ecf20Sopenharmony_ci	slgt_device_count = 0;
37388c2ecf20Sopenharmony_ci	if ((rc = pci_register_driver(&pci_driver)) < 0) {
37398c2ecf20Sopenharmony_ci		printk("%s pci_register_driver error=%d\n", driver_name, rc);
37408c2ecf20Sopenharmony_ci		goto error;
37418c2ecf20Sopenharmony_ci	}
37428c2ecf20Sopenharmony_ci	pci_registered = true;
37438c2ecf20Sopenharmony_ci
37448c2ecf20Sopenharmony_ci	if (!slgt_device_list)
37458c2ecf20Sopenharmony_ci		printk("%s no devices found\n",driver_name);
37468c2ecf20Sopenharmony_ci
37478c2ecf20Sopenharmony_ci	return 0;
37488c2ecf20Sopenharmony_ci
37498c2ecf20Sopenharmony_cierror:
37508c2ecf20Sopenharmony_ci	slgt_cleanup();
37518c2ecf20Sopenharmony_ci	return rc;
37528c2ecf20Sopenharmony_ci}
37538c2ecf20Sopenharmony_ci
37548c2ecf20Sopenharmony_cistatic void __exit slgt_exit(void)
37558c2ecf20Sopenharmony_ci{
37568c2ecf20Sopenharmony_ci	slgt_cleanup();
37578c2ecf20Sopenharmony_ci}
37588c2ecf20Sopenharmony_ci
37598c2ecf20Sopenharmony_cimodule_init(slgt_init);
37608c2ecf20Sopenharmony_cimodule_exit(slgt_exit);
37618c2ecf20Sopenharmony_ci
37628c2ecf20Sopenharmony_ci/*
37638c2ecf20Sopenharmony_ci * register access routines
37648c2ecf20Sopenharmony_ci */
37658c2ecf20Sopenharmony_ci
37668c2ecf20Sopenharmony_ci#define CALC_REGADDR() \
37678c2ecf20Sopenharmony_ci	unsigned long reg_addr = ((unsigned long)info->reg_addr) + addr; \
37688c2ecf20Sopenharmony_ci	if (addr >= 0x80) \
37698c2ecf20Sopenharmony_ci		reg_addr += (info->port_num) * 32; \
37708c2ecf20Sopenharmony_ci	else if (addr >= 0x40)	\
37718c2ecf20Sopenharmony_ci		reg_addr += (info->port_num) * 16;
37728c2ecf20Sopenharmony_ci
37738c2ecf20Sopenharmony_cistatic __u8 rd_reg8(struct slgt_info *info, unsigned int addr)
37748c2ecf20Sopenharmony_ci{
37758c2ecf20Sopenharmony_ci	CALC_REGADDR();
37768c2ecf20Sopenharmony_ci	return readb((void __iomem *)reg_addr);
37778c2ecf20Sopenharmony_ci}
37788c2ecf20Sopenharmony_ci
37798c2ecf20Sopenharmony_cistatic void wr_reg8(struct slgt_info *info, unsigned int addr, __u8 value)
37808c2ecf20Sopenharmony_ci{
37818c2ecf20Sopenharmony_ci	CALC_REGADDR();
37828c2ecf20Sopenharmony_ci	writeb(value, (void __iomem *)reg_addr);
37838c2ecf20Sopenharmony_ci}
37848c2ecf20Sopenharmony_ci
37858c2ecf20Sopenharmony_cistatic __u16 rd_reg16(struct slgt_info *info, unsigned int addr)
37868c2ecf20Sopenharmony_ci{
37878c2ecf20Sopenharmony_ci	CALC_REGADDR();
37888c2ecf20Sopenharmony_ci	return readw((void __iomem *)reg_addr);
37898c2ecf20Sopenharmony_ci}
37908c2ecf20Sopenharmony_ci
37918c2ecf20Sopenharmony_cistatic void wr_reg16(struct slgt_info *info, unsigned int addr, __u16 value)
37928c2ecf20Sopenharmony_ci{
37938c2ecf20Sopenharmony_ci	CALC_REGADDR();
37948c2ecf20Sopenharmony_ci	writew(value, (void __iomem *)reg_addr);
37958c2ecf20Sopenharmony_ci}
37968c2ecf20Sopenharmony_ci
37978c2ecf20Sopenharmony_cistatic __u32 rd_reg32(struct slgt_info *info, unsigned int addr)
37988c2ecf20Sopenharmony_ci{
37998c2ecf20Sopenharmony_ci	CALC_REGADDR();
38008c2ecf20Sopenharmony_ci	return readl((void __iomem *)reg_addr);
38018c2ecf20Sopenharmony_ci}
38028c2ecf20Sopenharmony_ci
38038c2ecf20Sopenharmony_cistatic void wr_reg32(struct slgt_info *info, unsigned int addr, __u32 value)
38048c2ecf20Sopenharmony_ci{
38058c2ecf20Sopenharmony_ci	CALC_REGADDR();
38068c2ecf20Sopenharmony_ci	writel(value, (void __iomem *)reg_addr);
38078c2ecf20Sopenharmony_ci}
38088c2ecf20Sopenharmony_ci
38098c2ecf20Sopenharmony_cistatic void rdma_reset(struct slgt_info *info)
38108c2ecf20Sopenharmony_ci{
38118c2ecf20Sopenharmony_ci	unsigned int i;
38128c2ecf20Sopenharmony_ci
38138c2ecf20Sopenharmony_ci	/* set reset bit */
38148c2ecf20Sopenharmony_ci	wr_reg32(info, RDCSR, BIT1);
38158c2ecf20Sopenharmony_ci
38168c2ecf20Sopenharmony_ci	/* wait for enable bit cleared */
38178c2ecf20Sopenharmony_ci	for(i=0 ; i < 1000 ; i++)
38188c2ecf20Sopenharmony_ci		if (!(rd_reg32(info, RDCSR) & BIT0))
38198c2ecf20Sopenharmony_ci			break;
38208c2ecf20Sopenharmony_ci}
38218c2ecf20Sopenharmony_ci
38228c2ecf20Sopenharmony_cistatic void tdma_reset(struct slgt_info *info)
38238c2ecf20Sopenharmony_ci{
38248c2ecf20Sopenharmony_ci	unsigned int i;
38258c2ecf20Sopenharmony_ci
38268c2ecf20Sopenharmony_ci	/* set reset bit */
38278c2ecf20Sopenharmony_ci	wr_reg32(info, TDCSR, BIT1);
38288c2ecf20Sopenharmony_ci
38298c2ecf20Sopenharmony_ci	/* wait for enable bit cleared */
38308c2ecf20Sopenharmony_ci	for(i=0 ; i < 1000 ; i++)
38318c2ecf20Sopenharmony_ci		if (!(rd_reg32(info, TDCSR) & BIT0))
38328c2ecf20Sopenharmony_ci			break;
38338c2ecf20Sopenharmony_ci}
38348c2ecf20Sopenharmony_ci
38358c2ecf20Sopenharmony_ci/*
38368c2ecf20Sopenharmony_ci * enable internal loopback
38378c2ecf20Sopenharmony_ci * TxCLK and RxCLK are generated from BRG
38388c2ecf20Sopenharmony_ci * and TxD is looped back to RxD internally.
38398c2ecf20Sopenharmony_ci */
38408c2ecf20Sopenharmony_cistatic void enable_loopback(struct slgt_info *info)
38418c2ecf20Sopenharmony_ci{
38428c2ecf20Sopenharmony_ci	/* SCR (serial control) BIT2=loopback enable */
38438c2ecf20Sopenharmony_ci	wr_reg16(info, SCR, (unsigned short)(rd_reg16(info, SCR) | BIT2));
38448c2ecf20Sopenharmony_ci
38458c2ecf20Sopenharmony_ci	if (info->params.mode != MGSL_MODE_ASYNC) {
38468c2ecf20Sopenharmony_ci		/* CCR (clock control)
38478c2ecf20Sopenharmony_ci		 * 07..05  tx clock source (010 = BRG)
38488c2ecf20Sopenharmony_ci		 * 04..02  rx clock source (010 = BRG)
38498c2ecf20Sopenharmony_ci		 * 01      auxclk enable   (0 = disable)
38508c2ecf20Sopenharmony_ci		 * 00      BRG enable      (1 = enable)
38518c2ecf20Sopenharmony_ci		 *
38528c2ecf20Sopenharmony_ci		 * 0100 1001
38538c2ecf20Sopenharmony_ci		 */
38548c2ecf20Sopenharmony_ci		wr_reg8(info, CCR, 0x49);
38558c2ecf20Sopenharmony_ci
38568c2ecf20Sopenharmony_ci		/* set speed if available, otherwise use default */
38578c2ecf20Sopenharmony_ci		if (info->params.clock_speed)
38588c2ecf20Sopenharmony_ci			set_rate(info, info->params.clock_speed);
38598c2ecf20Sopenharmony_ci		else
38608c2ecf20Sopenharmony_ci			set_rate(info, 3686400);
38618c2ecf20Sopenharmony_ci	}
38628c2ecf20Sopenharmony_ci}
38638c2ecf20Sopenharmony_ci
38648c2ecf20Sopenharmony_ci/*
38658c2ecf20Sopenharmony_ci *  set baud rate generator to specified rate
38668c2ecf20Sopenharmony_ci */
38678c2ecf20Sopenharmony_cistatic void set_rate(struct slgt_info *info, u32 rate)
38688c2ecf20Sopenharmony_ci{
38698c2ecf20Sopenharmony_ci	unsigned int div;
38708c2ecf20Sopenharmony_ci	unsigned int osc = info->base_clock;
38718c2ecf20Sopenharmony_ci
38728c2ecf20Sopenharmony_ci	/* div = osc/rate - 1
38738c2ecf20Sopenharmony_ci	 *
38748c2ecf20Sopenharmony_ci	 * Round div up if osc/rate is not integer to
38758c2ecf20Sopenharmony_ci	 * force to next slowest rate.
38768c2ecf20Sopenharmony_ci	 */
38778c2ecf20Sopenharmony_ci
38788c2ecf20Sopenharmony_ci	if (rate) {
38798c2ecf20Sopenharmony_ci		div = osc/rate;
38808c2ecf20Sopenharmony_ci		if (!(osc % rate) && div)
38818c2ecf20Sopenharmony_ci			div--;
38828c2ecf20Sopenharmony_ci		wr_reg16(info, BDR, (unsigned short)div);
38838c2ecf20Sopenharmony_ci	}
38848c2ecf20Sopenharmony_ci}
38858c2ecf20Sopenharmony_ci
38868c2ecf20Sopenharmony_cistatic void rx_stop(struct slgt_info *info)
38878c2ecf20Sopenharmony_ci{
38888c2ecf20Sopenharmony_ci	unsigned short val;
38898c2ecf20Sopenharmony_ci
38908c2ecf20Sopenharmony_ci	/* disable and reset receiver */
38918c2ecf20Sopenharmony_ci	val = rd_reg16(info, RCR) & ~BIT1;          /* clear enable bit */
38928c2ecf20Sopenharmony_ci	wr_reg16(info, RCR, (unsigned short)(val | BIT2)); /* set reset bit */
38938c2ecf20Sopenharmony_ci	wr_reg16(info, RCR, val);                  /* clear reset bit */
38948c2ecf20Sopenharmony_ci
38958c2ecf20Sopenharmony_ci	slgt_irq_off(info, IRQ_RXOVER + IRQ_RXDATA + IRQ_RXIDLE);
38968c2ecf20Sopenharmony_ci
38978c2ecf20Sopenharmony_ci	/* clear pending rx interrupts */
38988c2ecf20Sopenharmony_ci	wr_reg16(info, SSR, IRQ_RXIDLE + IRQ_RXOVER);
38998c2ecf20Sopenharmony_ci
39008c2ecf20Sopenharmony_ci	rdma_reset(info);
39018c2ecf20Sopenharmony_ci
39028c2ecf20Sopenharmony_ci	info->rx_enabled = false;
39038c2ecf20Sopenharmony_ci	info->rx_restart = false;
39048c2ecf20Sopenharmony_ci}
39058c2ecf20Sopenharmony_ci
39068c2ecf20Sopenharmony_cistatic void rx_start(struct slgt_info *info)
39078c2ecf20Sopenharmony_ci{
39088c2ecf20Sopenharmony_ci	unsigned short val;
39098c2ecf20Sopenharmony_ci
39108c2ecf20Sopenharmony_ci	slgt_irq_off(info, IRQ_RXOVER + IRQ_RXDATA);
39118c2ecf20Sopenharmony_ci
39128c2ecf20Sopenharmony_ci	/* clear pending rx overrun IRQ */
39138c2ecf20Sopenharmony_ci	wr_reg16(info, SSR, IRQ_RXOVER);
39148c2ecf20Sopenharmony_ci
39158c2ecf20Sopenharmony_ci	/* reset and disable receiver */
39168c2ecf20Sopenharmony_ci	val = rd_reg16(info, RCR) & ~BIT1; /* clear enable bit */
39178c2ecf20Sopenharmony_ci	wr_reg16(info, RCR, (unsigned short)(val | BIT2)); /* set reset bit */
39188c2ecf20Sopenharmony_ci	wr_reg16(info, RCR, val);                  /* clear reset bit */
39198c2ecf20Sopenharmony_ci
39208c2ecf20Sopenharmony_ci	rdma_reset(info);
39218c2ecf20Sopenharmony_ci	reset_rbufs(info);
39228c2ecf20Sopenharmony_ci
39238c2ecf20Sopenharmony_ci	if (info->rx_pio) {
39248c2ecf20Sopenharmony_ci		/* rx request when rx FIFO not empty */
39258c2ecf20Sopenharmony_ci		wr_reg16(info, SCR, (unsigned short)(rd_reg16(info, SCR) & ~BIT14));
39268c2ecf20Sopenharmony_ci		slgt_irq_on(info, IRQ_RXDATA);
39278c2ecf20Sopenharmony_ci		if (info->params.mode == MGSL_MODE_ASYNC) {
39288c2ecf20Sopenharmony_ci			/* enable saving of rx status */
39298c2ecf20Sopenharmony_ci			wr_reg32(info, RDCSR, BIT6);
39308c2ecf20Sopenharmony_ci		}
39318c2ecf20Sopenharmony_ci	} else {
39328c2ecf20Sopenharmony_ci		/* rx request when rx FIFO half full */
39338c2ecf20Sopenharmony_ci		wr_reg16(info, SCR, (unsigned short)(rd_reg16(info, SCR) | BIT14));
39348c2ecf20Sopenharmony_ci		/* set 1st descriptor address */
39358c2ecf20Sopenharmony_ci		wr_reg32(info, RDDAR, info->rbufs[0].pdesc);
39368c2ecf20Sopenharmony_ci
39378c2ecf20Sopenharmony_ci		if (info->params.mode != MGSL_MODE_ASYNC) {
39388c2ecf20Sopenharmony_ci			/* enable rx DMA and DMA interrupt */
39398c2ecf20Sopenharmony_ci			wr_reg32(info, RDCSR, (BIT2 + BIT0));
39408c2ecf20Sopenharmony_ci		} else {
39418c2ecf20Sopenharmony_ci			/* enable saving of rx status, rx DMA and DMA interrupt */
39428c2ecf20Sopenharmony_ci			wr_reg32(info, RDCSR, (BIT6 + BIT2 + BIT0));
39438c2ecf20Sopenharmony_ci		}
39448c2ecf20Sopenharmony_ci	}
39458c2ecf20Sopenharmony_ci
39468c2ecf20Sopenharmony_ci	slgt_irq_on(info, IRQ_RXOVER);
39478c2ecf20Sopenharmony_ci
39488c2ecf20Sopenharmony_ci	/* enable receiver */
39498c2ecf20Sopenharmony_ci	wr_reg16(info, RCR, (unsigned short)(rd_reg16(info, RCR) | BIT1));
39508c2ecf20Sopenharmony_ci
39518c2ecf20Sopenharmony_ci	info->rx_restart = false;
39528c2ecf20Sopenharmony_ci	info->rx_enabled = true;
39538c2ecf20Sopenharmony_ci}
39548c2ecf20Sopenharmony_ci
39558c2ecf20Sopenharmony_cistatic void tx_start(struct slgt_info *info)
39568c2ecf20Sopenharmony_ci{
39578c2ecf20Sopenharmony_ci	if (!info->tx_enabled) {
39588c2ecf20Sopenharmony_ci		wr_reg16(info, TCR,
39598c2ecf20Sopenharmony_ci			 (unsigned short)((rd_reg16(info, TCR) | BIT1) & ~BIT2));
39608c2ecf20Sopenharmony_ci		info->tx_enabled = true;
39618c2ecf20Sopenharmony_ci	}
39628c2ecf20Sopenharmony_ci
39638c2ecf20Sopenharmony_ci	if (desc_count(info->tbufs[info->tbuf_start])) {
39648c2ecf20Sopenharmony_ci		info->drop_rts_on_tx_done = false;
39658c2ecf20Sopenharmony_ci
39668c2ecf20Sopenharmony_ci		if (info->params.mode != MGSL_MODE_ASYNC) {
39678c2ecf20Sopenharmony_ci			if (info->params.flags & HDLC_FLAG_AUTO_RTS) {
39688c2ecf20Sopenharmony_ci				get_gtsignals(info);
39698c2ecf20Sopenharmony_ci				if (!(info->signals & SerialSignal_RTS)) {
39708c2ecf20Sopenharmony_ci					info->signals |= SerialSignal_RTS;
39718c2ecf20Sopenharmony_ci					set_gtsignals(info);
39728c2ecf20Sopenharmony_ci					info->drop_rts_on_tx_done = true;
39738c2ecf20Sopenharmony_ci				}
39748c2ecf20Sopenharmony_ci			}
39758c2ecf20Sopenharmony_ci
39768c2ecf20Sopenharmony_ci			slgt_irq_off(info, IRQ_TXDATA);
39778c2ecf20Sopenharmony_ci			slgt_irq_on(info, IRQ_TXUNDER + IRQ_TXIDLE);
39788c2ecf20Sopenharmony_ci			/* clear tx idle and underrun status bits */
39798c2ecf20Sopenharmony_ci			wr_reg16(info, SSR, (unsigned short)(IRQ_TXIDLE + IRQ_TXUNDER));
39808c2ecf20Sopenharmony_ci		} else {
39818c2ecf20Sopenharmony_ci			slgt_irq_off(info, IRQ_TXDATA);
39828c2ecf20Sopenharmony_ci			slgt_irq_on(info, IRQ_TXIDLE);
39838c2ecf20Sopenharmony_ci			/* clear tx idle status bit */
39848c2ecf20Sopenharmony_ci			wr_reg16(info, SSR, IRQ_TXIDLE);
39858c2ecf20Sopenharmony_ci		}
39868c2ecf20Sopenharmony_ci		/* set 1st descriptor address and start DMA */
39878c2ecf20Sopenharmony_ci		wr_reg32(info, TDDAR, info->tbufs[info->tbuf_start].pdesc);
39888c2ecf20Sopenharmony_ci		wr_reg32(info, TDCSR, BIT2 + BIT0);
39898c2ecf20Sopenharmony_ci		info->tx_active = true;
39908c2ecf20Sopenharmony_ci	}
39918c2ecf20Sopenharmony_ci}
39928c2ecf20Sopenharmony_ci
39938c2ecf20Sopenharmony_cistatic void tx_stop(struct slgt_info *info)
39948c2ecf20Sopenharmony_ci{
39958c2ecf20Sopenharmony_ci	unsigned short val;
39968c2ecf20Sopenharmony_ci
39978c2ecf20Sopenharmony_ci	del_timer(&info->tx_timer);
39988c2ecf20Sopenharmony_ci
39998c2ecf20Sopenharmony_ci	tdma_reset(info);
40008c2ecf20Sopenharmony_ci
40018c2ecf20Sopenharmony_ci	/* reset and disable transmitter */
40028c2ecf20Sopenharmony_ci	val = rd_reg16(info, TCR) & ~BIT1;          /* clear enable bit */
40038c2ecf20Sopenharmony_ci	wr_reg16(info, TCR, (unsigned short)(val | BIT2)); /* set reset bit */
40048c2ecf20Sopenharmony_ci
40058c2ecf20Sopenharmony_ci	slgt_irq_off(info, IRQ_TXDATA + IRQ_TXIDLE + IRQ_TXUNDER);
40068c2ecf20Sopenharmony_ci
40078c2ecf20Sopenharmony_ci	/* clear tx idle and underrun status bit */
40088c2ecf20Sopenharmony_ci	wr_reg16(info, SSR, (unsigned short)(IRQ_TXIDLE + IRQ_TXUNDER));
40098c2ecf20Sopenharmony_ci
40108c2ecf20Sopenharmony_ci	reset_tbufs(info);
40118c2ecf20Sopenharmony_ci
40128c2ecf20Sopenharmony_ci	info->tx_enabled = false;
40138c2ecf20Sopenharmony_ci	info->tx_active = false;
40148c2ecf20Sopenharmony_ci}
40158c2ecf20Sopenharmony_ci
40168c2ecf20Sopenharmony_cistatic void reset_port(struct slgt_info *info)
40178c2ecf20Sopenharmony_ci{
40188c2ecf20Sopenharmony_ci	if (!info->reg_addr)
40198c2ecf20Sopenharmony_ci		return;
40208c2ecf20Sopenharmony_ci
40218c2ecf20Sopenharmony_ci	tx_stop(info);
40228c2ecf20Sopenharmony_ci	rx_stop(info);
40238c2ecf20Sopenharmony_ci
40248c2ecf20Sopenharmony_ci	info->signals &= ~(SerialSignal_RTS | SerialSignal_DTR);
40258c2ecf20Sopenharmony_ci	set_gtsignals(info);
40268c2ecf20Sopenharmony_ci
40278c2ecf20Sopenharmony_ci	slgt_irq_off(info, IRQ_ALL | IRQ_MASTER);
40288c2ecf20Sopenharmony_ci}
40298c2ecf20Sopenharmony_ci
40308c2ecf20Sopenharmony_cistatic void reset_adapter(struct slgt_info *info)
40318c2ecf20Sopenharmony_ci{
40328c2ecf20Sopenharmony_ci	int i;
40338c2ecf20Sopenharmony_ci	for (i=0; i < info->port_count; ++i) {
40348c2ecf20Sopenharmony_ci		if (info->port_array[i])
40358c2ecf20Sopenharmony_ci			reset_port(info->port_array[i]);
40368c2ecf20Sopenharmony_ci	}
40378c2ecf20Sopenharmony_ci}
40388c2ecf20Sopenharmony_ci
40398c2ecf20Sopenharmony_cistatic void async_mode(struct slgt_info *info)
40408c2ecf20Sopenharmony_ci{
40418c2ecf20Sopenharmony_ci  	unsigned short val;
40428c2ecf20Sopenharmony_ci
40438c2ecf20Sopenharmony_ci	slgt_irq_off(info, IRQ_ALL | IRQ_MASTER);
40448c2ecf20Sopenharmony_ci	tx_stop(info);
40458c2ecf20Sopenharmony_ci	rx_stop(info);
40468c2ecf20Sopenharmony_ci
40478c2ecf20Sopenharmony_ci	/* TCR (tx control)
40488c2ecf20Sopenharmony_ci	 *
40498c2ecf20Sopenharmony_ci	 * 15..13  mode, 010=async
40508c2ecf20Sopenharmony_ci	 * 12..10  encoding, 000=NRZ
40518c2ecf20Sopenharmony_ci	 * 09      parity enable
40528c2ecf20Sopenharmony_ci	 * 08      1=odd parity, 0=even parity
40538c2ecf20Sopenharmony_ci	 * 07      1=RTS driver control
40548c2ecf20Sopenharmony_ci	 * 06      1=break enable
40558c2ecf20Sopenharmony_ci	 * 05..04  character length
40568c2ecf20Sopenharmony_ci	 *         00=5 bits
40578c2ecf20Sopenharmony_ci	 *         01=6 bits
40588c2ecf20Sopenharmony_ci	 *         10=7 bits
40598c2ecf20Sopenharmony_ci	 *         11=8 bits
40608c2ecf20Sopenharmony_ci	 * 03      0=1 stop bit, 1=2 stop bits
40618c2ecf20Sopenharmony_ci	 * 02      reset
40628c2ecf20Sopenharmony_ci	 * 01      enable
40638c2ecf20Sopenharmony_ci	 * 00      auto-CTS enable
40648c2ecf20Sopenharmony_ci	 */
40658c2ecf20Sopenharmony_ci	val = 0x4000;
40668c2ecf20Sopenharmony_ci
40678c2ecf20Sopenharmony_ci	if (info->if_mode & MGSL_INTERFACE_RTS_EN)
40688c2ecf20Sopenharmony_ci		val |= BIT7;
40698c2ecf20Sopenharmony_ci
40708c2ecf20Sopenharmony_ci	if (info->params.parity != ASYNC_PARITY_NONE) {
40718c2ecf20Sopenharmony_ci		val |= BIT9;
40728c2ecf20Sopenharmony_ci		if (info->params.parity == ASYNC_PARITY_ODD)
40738c2ecf20Sopenharmony_ci			val |= BIT8;
40748c2ecf20Sopenharmony_ci	}
40758c2ecf20Sopenharmony_ci
40768c2ecf20Sopenharmony_ci	switch (info->params.data_bits)
40778c2ecf20Sopenharmony_ci	{
40788c2ecf20Sopenharmony_ci	case 6: val |= BIT4; break;
40798c2ecf20Sopenharmony_ci	case 7: val |= BIT5; break;
40808c2ecf20Sopenharmony_ci	case 8: val |= BIT5 + BIT4; break;
40818c2ecf20Sopenharmony_ci	}
40828c2ecf20Sopenharmony_ci
40838c2ecf20Sopenharmony_ci	if (info->params.stop_bits != 1)
40848c2ecf20Sopenharmony_ci		val |= BIT3;
40858c2ecf20Sopenharmony_ci
40868c2ecf20Sopenharmony_ci	if (info->params.flags & HDLC_FLAG_AUTO_CTS)
40878c2ecf20Sopenharmony_ci		val |= BIT0;
40888c2ecf20Sopenharmony_ci
40898c2ecf20Sopenharmony_ci	wr_reg16(info, TCR, val);
40908c2ecf20Sopenharmony_ci
40918c2ecf20Sopenharmony_ci	/* RCR (rx control)
40928c2ecf20Sopenharmony_ci	 *
40938c2ecf20Sopenharmony_ci	 * 15..13  mode, 010=async
40948c2ecf20Sopenharmony_ci	 * 12..10  encoding, 000=NRZ
40958c2ecf20Sopenharmony_ci	 * 09      parity enable
40968c2ecf20Sopenharmony_ci	 * 08      1=odd parity, 0=even parity
40978c2ecf20Sopenharmony_ci	 * 07..06  reserved, must be 0
40988c2ecf20Sopenharmony_ci	 * 05..04  character length
40998c2ecf20Sopenharmony_ci	 *         00=5 bits
41008c2ecf20Sopenharmony_ci	 *         01=6 bits
41018c2ecf20Sopenharmony_ci	 *         10=7 bits
41028c2ecf20Sopenharmony_ci	 *         11=8 bits
41038c2ecf20Sopenharmony_ci	 * 03      reserved, must be zero
41048c2ecf20Sopenharmony_ci	 * 02      reset
41058c2ecf20Sopenharmony_ci	 * 01      enable
41068c2ecf20Sopenharmony_ci	 * 00      auto-DCD enable
41078c2ecf20Sopenharmony_ci	 */
41088c2ecf20Sopenharmony_ci	val = 0x4000;
41098c2ecf20Sopenharmony_ci
41108c2ecf20Sopenharmony_ci	if (info->params.parity != ASYNC_PARITY_NONE) {
41118c2ecf20Sopenharmony_ci		val |= BIT9;
41128c2ecf20Sopenharmony_ci		if (info->params.parity == ASYNC_PARITY_ODD)
41138c2ecf20Sopenharmony_ci			val |= BIT8;
41148c2ecf20Sopenharmony_ci	}
41158c2ecf20Sopenharmony_ci
41168c2ecf20Sopenharmony_ci	switch (info->params.data_bits)
41178c2ecf20Sopenharmony_ci	{
41188c2ecf20Sopenharmony_ci	case 6: val |= BIT4; break;
41198c2ecf20Sopenharmony_ci	case 7: val |= BIT5; break;
41208c2ecf20Sopenharmony_ci	case 8: val |= BIT5 + BIT4; break;
41218c2ecf20Sopenharmony_ci	}
41228c2ecf20Sopenharmony_ci
41238c2ecf20Sopenharmony_ci	if (info->params.flags & HDLC_FLAG_AUTO_DCD)
41248c2ecf20Sopenharmony_ci		val |= BIT0;
41258c2ecf20Sopenharmony_ci
41268c2ecf20Sopenharmony_ci	wr_reg16(info, RCR, val);
41278c2ecf20Sopenharmony_ci
41288c2ecf20Sopenharmony_ci	/* CCR (clock control)
41298c2ecf20Sopenharmony_ci	 *
41308c2ecf20Sopenharmony_ci	 * 07..05  011 = tx clock source is BRG/16
41318c2ecf20Sopenharmony_ci	 * 04..02  010 = rx clock source is BRG
41328c2ecf20Sopenharmony_ci	 * 01      0 = auxclk disabled
41338c2ecf20Sopenharmony_ci	 * 00      1 = BRG enabled
41348c2ecf20Sopenharmony_ci	 *
41358c2ecf20Sopenharmony_ci	 * 0110 1001
41368c2ecf20Sopenharmony_ci	 */
41378c2ecf20Sopenharmony_ci	wr_reg8(info, CCR, 0x69);
41388c2ecf20Sopenharmony_ci
41398c2ecf20Sopenharmony_ci	msc_set_vcr(info);
41408c2ecf20Sopenharmony_ci
41418c2ecf20Sopenharmony_ci	/* SCR (serial control)
41428c2ecf20Sopenharmony_ci	 *
41438c2ecf20Sopenharmony_ci	 * 15  1=tx req on FIFO half empty
41448c2ecf20Sopenharmony_ci	 * 14  1=rx req on FIFO half full
41458c2ecf20Sopenharmony_ci	 * 13  tx data  IRQ enable
41468c2ecf20Sopenharmony_ci	 * 12  tx idle  IRQ enable
41478c2ecf20Sopenharmony_ci	 * 11  rx break on IRQ enable
41488c2ecf20Sopenharmony_ci	 * 10  rx data  IRQ enable
41498c2ecf20Sopenharmony_ci	 * 09  rx break off IRQ enable
41508c2ecf20Sopenharmony_ci	 * 08  overrun  IRQ enable
41518c2ecf20Sopenharmony_ci	 * 07  DSR      IRQ enable
41528c2ecf20Sopenharmony_ci	 * 06  CTS      IRQ enable
41538c2ecf20Sopenharmony_ci	 * 05  DCD      IRQ enable
41548c2ecf20Sopenharmony_ci	 * 04  RI       IRQ enable
41558c2ecf20Sopenharmony_ci	 * 03  0=16x sampling, 1=8x sampling
41568c2ecf20Sopenharmony_ci	 * 02  1=txd->rxd internal loopback enable
41578c2ecf20Sopenharmony_ci	 * 01  reserved, must be zero
41588c2ecf20Sopenharmony_ci	 * 00  1=master IRQ enable
41598c2ecf20Sopenharmony_ci	 */
41608c2ecf20Sopenharmony_ci	val = BIT15 + BIT14 + BIT0;
41618c2ecf20Sopenharmony_ci	/* JCR[8] : 1 = x8 async mode feature available */
41628c2ecf20Sopenharmony_ci	if ((rd_reg32(info, JCR) & BIT8) && info->params.data_rate &&
41638c2ecf20Sopenharmony_ci	    ((info->base_clock < (info->params.data_rate * 16)) ||
41648c2ecf20Sopenharmony_ci	     (info->base_clock % (info->params.data_rate * 16)))) {
41658c2ecf20Sopenharmony_ci		/* use 8x sampling */
41668c2ecf20Sopenharmony_ci		val |= BIT3;
41678c2ecf20Sopenharmony_ci		set_rate(info, info->params.data_rate * 8);
41688c2ecf20Sopenharmony_ci	} else {
41698c2ecf20Sopenharmony_ci		/* use 16x sampling */
41708c2ecf20Sopenharmony_ci		set_rate(info, info->params.data_rate * 16);
41718c2ecf20Sopenharmony_ci	}
41728c2ecf20Sopenharmony_ci	wr_reg16(info, SCR, val);
41738c2ecf20Sopenharmony_ci
41748c2ecf20Sopenharmony_ci	slgt_irq_on(info, IRQ_RXBREAK | IRQ_RXOVER);
41758c2ecf20Sopenharmony_ci
41768c2ecf20Sopenharmony_ci	if (info->params.loopback)
41778c2ecf20Sopenharmony_ci		enable_loopback(info);
41788c2ecf20Sopenharmony_ci}
41798c2ecf20Sopenharmony_ci
41808c2ecf20Sopenharmony_cistatic void sync_mode(struct slgt_info *info)
41818c2ecf20Sopenharmony_ci{
41828c2ecf20Sopenharmony_ci	unsigned short val;
41838c2ecf20Sopenharmony_ci
41848c2ecf20Sopenharmony_ci	slgt_irq_off(info, IRQ_ALL | IRQ_MASTER);
41858c2ecf20Sopenharmony_ci	tx_stop(info);
41868c2ecf20Sopenharmony_ci	rx_stop(info);
41878c2ecf20Sopenharmony_ci
41888c2ecf20Sopenharmony_ci	/* TCR (tx control)
41898c2ecf20Sopenharmony_ci	 *
41908c2ecf20Sopenharmony_ci	 * 15..13  mode
41918c2ecf20Sopenharmony_ci	 *         000=HDLC/SDLC
41928c2ecf20Sopenharmony_ci	 *         001=raw bit synchronous
41938c2ecf20Sopenharmony_ci	 *         010=asynchronous/isochronous
41948c2ecf20Sopenharmony_ci	 *         011=monosync byte synchronous
41958c2ecf20Sopenharmony_ci	 *         100=bisync byte synchronous
41968c2ecf20Sopenharmony_ci	 *         101=xsync byte synchronous
41978c2ecf20Sopenharmony_ci	 * 12..10  encoding
41988c2ecf20Sopenharmony_ci	 * 09      CRC enable
41998c2ecf20Sopenharmony_ci	 * 08      CRC32
42008c2ecf20Sopenharmony_ci	 * 07      1=RTS driver control
42018c2ecf20Sopenharmony_ci	 * 06      preamble enable
42028c2ecf20Sopenharmony_ci	 * 05..04  preamble length
42038c2ecf20Sopenharmony_ci	 * 03      share open/close flag
42048c2ecf20Sopenharmony_ci	 * 02      reset
42058c2ecf20Sopenharmony_ci	 * 01      enable
42068c2ecf20Sopenharmony_ci	 * 00      auto-CTS enable
42078c2ecf20Sopenharmony_ci	 */
42088c2ecf20Sopenharmony_ci	val = BIT2;
42098c2ecf20Sopenharmony_ci
42108c2ecf20Sopenharmony_ci	switch(info->params.mode) {
42118c2ecf20Sopenharmony_ci	case MGSL_MODE_XSYNC:
42128c2ecf20Sopenharmony_ci		val |= BIT15 + BIT13;
42138c2ecf20Sopenharmony_ci		break;
42148c2ecf20Sopenharmony_ci	case MGSL_MODE_MONOSYNC: val |= BIT14 + BIT13; break;
42158c2ecf20Sopenharmony_ci	case MGSL_MODE_BISYNC:   val |= BIT15; break;
42168c2ecf20Sopenharmony_ci	case MGSL_MODE_RAW:      val |= BIT13; break;
42178c2ecf20Sopenharmony_ci	}
42188c2ecf20Sopenharmony_ci	if (info->if_mode & MGSL_INTERFACE_RTS_EN)
42198c2ecf20Sopenharmony_ci		val |= BIT7;
42208c2ecf20Sopenharmony_ci
42218c2ecf20Sopenharmony_ci	switch(info->params.encoding)
42228c2ecf20Sopenharmony_ci	{
42238c2ecf20Sopenharmony_ci	case HDLC_ENCODING_NRZB:          val |= BIT10; break;
42248c2ecf20Sopenharmony_ci	case HDLC_ENCODING_NRZI_MARK:     val |= BIT11; break;
42258c2ecf20Sopenharmony_ci	case HDLC_ENCODING_NRZI:          val |= BIT11 + BIT10; break;
42268c2ecf20Sopenharmony_ci	case HDLC_ENCODING_BIPHASE_MARK:  val |= BIT12; break;
42278c2ecf20Sopenharmony_ci	case HDLC_ENCODING_BIPHASE_SPACE: val |= BIT12 + BIT10; break;
42288c2ecf20Sopenharmony_ci	case HDLC_ENCODING_BIPHASE_LEVEL: val |= BIT12 + BIT11; break;
42298c2ecf20Sopenharmony_ci	case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: val |= BIT12 + BIT11 + BIT10; break;
42308c2ecf20Sopenharmony_ci	}
42318c2ecf20Sopenharmony_ci
42328c2ecf20Sopenharmony_ci	switch (info->params.crc_type & HDLC_CRC_MASK)
42338c2ecf20Sopenharmony_ci	{
42348c2ecf20Sopenharmony_ci	case HDLC_CRC_16_CCITT: val |= BIT9; break;
42358c2ecf20Sopenharmony_ci	case HDLC_CRC_32_CCITT: val |= BIT9 + BIT8; break;
42368c2ecf20Sopenharmony_ci	}
42378c2ecf20Sopenharmony_ci
42388c2ecf20Sopenharmony_ci	if (info->params.preamble != HDLC_PREAMBLE_PATTERN_NONE)
42398c2ecf20Sopenharmony_ci		val |= BIT6;
42408c2ecf20Sopenharmony_ci
42418c2ecf20Sopenharmony_ci	switch (info->params.preamble_length)
42428c2ecf20Sopenharmony_ci	{
42438c2ecf20Sopenharmony_ci	case HDLC_PREAMBLE_LENGTH_16BITS: val |= BIT5; break;
42448c2ecf20Sopenharmony_ci	case HDLC_PREAMBLE_LENGTH_32BITS: val |= BIT4; break;
42458c2ecf20Sopenharmony_ci	case HDLC_PREAMBLE_LENGTH_64BITS: val |= BIT5 + BIT4; break;
42468c2ecf20Sopenharmony_ci	}
42478c2ecf20Sopenharmony_ci
42488c2ecf20Sopenharmony_ci	if (info->params.flags & HDLC_FLAG_AUTO_CTS)
42498c2ecf20Sopenharmony_ci		val |= BIT0;
42508c2ecf20Sopenharmony_ci
42518c2ecf20Sopenharmony_ci	wr_reg16(info, TCR, val);
42528c2ecf20Sopenharmony_ci
42538c2ecf20Sopenharmony_ci	/* TPR (transmit preamble) */
42548c2ecf20Sopenharmony_ci
42558c2ecf20Sopenharmony_ci	switch (info->params.preamble)
42568c2ecf20Sopenharmony_ci	{
42578c2ecf20Sopenharmony_ci	case HDLC_PREAMBLE_PATTERN_FLAGS: val = 0x7e; break;
42588c2ecf20Sopenharmony_ci	case HDLC_PREAMBLE_PATTERN_ONES:  val = 0xff; break;
42598c2ecf20Sopenharmony_ci	case HDLC_PREAMBLE_PATTERN_ZEROS: val = 0x00; break;
42608c2ecf20Sopenharmony_ci	case HDLC_PREAMBLE_PATTERN_10:    val = 0x55; break;
42618c2ecf20Sopenharmony_ci	case HDLC_PREAMBLE_PATTERN_01:    val = 0xaa; break;
42628c2ecf20Sopenharmony_ci	default:                          val = 0x7e; break;
42638c2ecf20Sopenharmony_ci	}
42648c2ecf20Sopenharmony_ci	wr_reg8(info, TPR, (unsigned char)val);
42658c2ecf20Sopenharmony_ci
42668c2ecf20Sopenharmony_ci	/* RCR (rx control)
42678c2ecf20Sopenharmony_ci	 *
42688c2ecf20Sopenharmony_ci	 * 15..13  mode
42698c2ecf20Sopenharmony_ci	 *         000=HDLC/SDLC
42708c2ecf20Sopenharmony_ci	 *         001=raw bit synchronous
42718c2ecf20Sopenharmony_ci	 *         010=asynchronous/isochronous
42728c2ecf20Sopenharmony_ci	 *         011=monosync byte synchronous
42738c2ecf20Sopenharmony_ci	 *         100=bisync byte synchronous
42748c2ecf20Sopenharmony_ci	 *         101=xsync byte synchronous
42758c2ecf20Sopenharmony_ci	 * 12..10  encoding
42768c2ecf20Sopenharmony_ci	 * 09      CRC enable
42778c2ecf20Sopenharmony_ci	 * 08      CRC32
42788c2ecf20Sopenharmony_ci	 * 07..03  reserved, must be 0
42798c2ecf20Sopenharmony_ci	 * 02      reset
42808c2ecf20Sopenharmony_ci	 * 01      enable
42818c2ecf20Sopenharmony_ci	 * 00      auto-DCD enable
42828c2ecf20Sopenharmony_ci	 */
42838c2ecf20Sopenharmony_ci	val = 0;
42848c2ecf20Sopenharmony_ci
42858c2ecf20Sopenharmony_ci	switch(info->params.mode) {
42868c2ecf20Sopenharmony_ci	case MGSL_MODE_XSYNC:
42878c2ecf20Sopenharmony_ci		val |= BIT15 + BIT13;
42888c2ecf20Sopenharmony_ci		break;
42898c2ecf20Sopenharmony_ci	case MGSL_MODE_MONOSYNC: val |= BIT14 + BIT13; break;
42908c2ecf20Sopenharmony_ci	case MGSL_MODE_BISYNC:   val |= BIT15; break;
42918c2ecf20Sopenharmony_ci	case MGSL_MODE_RAW:      val |= BIT13; break;
42928c2ecf20Sopenharmony_ci	}
42938c2ecf20Sopenharmony_ci
42948c2ecf20Sopenharmony_ci	switch(info->params.encoding)
42958c2ecf20Sopenharmony_ci	{
42968c2ecf20Sopenharmony_ci	case HDLC_ENCODING_NRZB:          val |= BIT10; break;
42978c2ecf20Sopenharmony_ci	case HDLC_ENCODING_NRZI_MARK:     val |= BIT11; break;
42988c2ecf20Sopenharmony_ci	case HDLC_ENCODING_NRZI:          val |= BIT11 + BIT10; break;
42998c2ecf20Sopenharmony_ci	case HDLC_ENCODING_BIPHASE_MARK:  val |= BIT12; break;
43008c2ecf20Sopenharmony_ci	case HDLC_ENCODING_BIPHASE_SPACE: val |= BIT12 + BIT10; break;
43018c2ecf20Sopenharmony_ci	case HDLC_ENCODING_BIPHASE_LEVEL: val |= BIT12 + BIT11; break;
43028c2ecf20Sopenharmony_ci	case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: val |= BIT12 + BIT11 + BIT10; break;
43038c2ecf20Sopenharmony_ci	}
43048c2ecf20Sopenharmony_ci
43058c2ecf20Sopenharmony_ci	switch (info->params.crc_type & HDLC_CRC_MASK)
43068c2ecf20Sopenharmony_ci	{
43078c2ecf20Sopenharmony_ci	case HDLC_CRC_16_CCITT: val |= BIT9; break;
43088c2ecf20Sopenharmony_ci	case HDLC_CRC_32_CCITT: val |= BIT9 + BIT8; break;
43098c2ecf20Sopenharmony_ci	}
43108c2ecf20Sopenharmony_ci
43118c2ecf20Sopenharmony_ci	if (info->params.flags & HDLC_FLAG_AUTO_DCD)
43128c2ecf20Sopenharmony_ci		val |= BIT0;
43138c2ecf20Sopenharmony_ci
43148c2ecf20Sopenharmony_ci	wr_reg16(info, RCR, val);
43158c2ecf20Sopenharmony_ci
43168c2ecf20Sopenharmony_ci	/* CCR (clock control)
43178c2ecf20Sopenharmony_ci	 *
43188c2ecf20Sopenharmony_ci	 * 07..05  tx clock source
43198c2ecf20Sopenharmony_ci	 * 04..02  rx clock source
43208c2ecf20Sopenharmony_ci	 * 01      auxclk enable
43218c2ecf20Sopenharmony_ci	 * 00      BRG enable
43228c2ecf20Sopenharmony_ci	 */
43238c2ecf20Sopenharmony_ci	val = 0;
43248c2ecf20Sopenharmony_ci
43258c2ecf20Sopenharmony_ci	if (info->params.flags & HDLC_FLAG_TXC_BRG)
43268c2ecf20Sopenharmony_ci	{
43278c2ecf20Sopenharmony_ci		// when RxC source is DPLL, BRG generates 16X DPLL
43288c2ecf20Sopenharmony_ci		// reference clock, so take TxC from BRG/16 to get
43298c2ecf20Sopenharmony_ci		// transmit clock at actual data rate
43308c2ecf20Sopenharmony_ci		if (info->params.flags & HDLC_FLAG_RXC_DPLL)
43318c2ecf20Sopenharmony_ci			val |= BIT6 + BIT5;	/* 011, txclk = BRG/16 */
43328c2ecf20Sopenharmony_ci		else
43338c2ecf20Sopenharmony_ci			val |= BIT6;	/* 010, txclk = BRG */
43348c2ecf20Sopenharmony_ci	}
43358c2ecf20Sopenharmony_ci	else if (info->params.flags & HDLC_FLAG_TXC_DPLL)
43368c2ecf20Sopenharmony_ci		val |= BIT7;	/* 100, txclk = DPLL Input */
43378c2ecf20Sopenharmony_ci	else if (info->params.flags & HDLC_FLAG_TXC_RXCPIN)
43388c2ecf20Sopenharmony_ci		val |= BIT5;	/* 001, txclk = RXC Input */
43398c2ecf20Sopenharmony_ci
43408c2ecf20Sopenharmony_ci	if (info->params.flags & HDLC_FLAG_RXC_BRG)
43418c2ecf20Sopenharmony_ci		val |= BIT3;	/* 010, rxclk = BRG */
43428c2ecf20Sopenharmony_ci	else if (info->params.flags & HDLC_FLAG_RXC_DPLL)
43438c2ecf20Sopenharmony_ci		val |= BIT4;	/* 100, rxclk = DPLL */
43448c2ecf20Sopenharmony_ci	else if (info->params.flags & HDLC_FLAG_RXC_TXCPIN)
43458c2ecf20Sopenharmony_ci		val |= BIT2;	/* 001, rxclk = TXC Input */
43468c2ecf20Sopenharmony_ci
43478c2ecf20Sopenharmony_ci	if (info->params.clock_speed)
43488c2ecf20Sopenharmony_ci		val |= BIT1 + BIT0;
43498c2ecf20Sopenharmony_ci
43508c2ecf20Sopenharmony_ci	wr_reg8(info, CCR, (unsigned char)val);
43518c2ecf20Sopenharmony_ci
43528c2ecf20Sopenharmony_ci	if (info->params.flags & (HDLC_FLAG_TXC_DPLL + HDLC_FLAG_RXC_DPLL))
43538c2ecf20Sopenharmony_ci	{
43548c2ecf20Sopenharmony_ci		// program DPLL mode
43558c2ecf20Sopenharmony_ci		switch(info->params.encoding)
43568c2ecf20Sopenharmony_ci		{
43578c2ecf20Sopenharmony_ci		case HDLC_ENCODING_BIPHASE_MARK:
43588c2ecf20Sopenharmony_ci		case HDLC_ENCODING_BIPHASE_SPACE:
43598c2ecf20Sopenharmony_ci			val = BIT7; break;
43608c2ecf20Sopenharmony_ci		case HDLC_ENCODING_BIPHASE_LEVEL:
43618c2ecf20Sopenharmony_ci		case HDLC_ENCODING_DIFF_BIPHASE_LEVEL:
43628c2ecf20Sopenharmony_ci			val = BIT7 + BIT6; break;
43638c2ecf20Sopenharmony_ci		default: val = BIT6;	// NRZ encodings
43648c2ecf20Sopenharmony_ci		}
43658c2ecf20Sopenharmony_ci		wr_reg16(info, RCR, (unsigned short)(rd_reg16(info, RCR) | val));
43668c2ecf20Sopenharmony_ci
43678c2ecf20Sopenharmony_ci		// DPLL requires a 16X reference clock from BRG
43688c2ecf20Sopenharmony_ci		set_rate(info, info->params.clock_speed * 16);
43698c2ecf20Sopenharmony_ci	}
43708c2ecf20Sopenharmony_ci	else
43718c2ecf20Sopenharmony_ci		set_rate(info, info->params.clock_speed);
43728c2ecf20Sopenharmony_ci
43738c2ecf20Sopenharmony_ci	tx_set_idle(info);
43748c2ecf20Sopenharmony_ci
43758c2ecf20Sopenharmony_ci	msc_set_vcr(info);
43768c2ecf20Sopenharmony_ci
43778c2ecf20Sopenharmony_ci	/* SCR (serial control)
43788c2ecf20Sopenharmony_ci	 *
43798c2ecf20Sopenharmony_ci	 * 15  1=tx req on FIFO half empty
43808c2ecf20Sopenharmony_ci	 * 14  1=rx req on FIFO half full
43818c2ecf20Sopenharmony_ci	 * 13  tx data  IRQ enable
43828c2ecf20Sopenharmony_ci	 * 12  tx idle  IRQ enable
43838c2ecf20Sopenharmony_ci	 * 11  underrun IRQ enable
43848c2ecf20Sopenharmony_ci	 * 10  rx data  IRQ enable
43858c2ecf20Sopenharmony_ci	 * 09  rx idle  IRQ enable
43868c2ecf20Sopenharmony_ci	 * 08  overrun  IRQ enable
43878c2ecf20Sopenharmony_ci	 * 07  DSR      IRQ enable
43888c2ecf20Sopenharmony_ci	 * 06  CTS      IRQ enable
43898c2ecf20Sopenharmony_ci	 * 05  DCD      IRQ enable
43908c2ecf20Sopenharmony_ci	 * 04  RI       IRQ enable
43918c2ecf20Sopenharmony_ci	 * 03  reserved, must be zero
43928c2ecf20Sopenharmony_ci	 * 02  1=txd->rxd internal loopback enable
43938c2ecf20Sopenharmony_ci	 * 01  reserved, must be zero
43948c2ecf20Sopenharmony_ci	 * 00  1=master IRQ enable
43958c2ecf20Sopenharmony_ci	 */
43968c2ecf20Sopenharmony_ci	wr_reg16(info, SCR, BIT15 + BIT14 + BIT0);
43978c2ecf20Sopenharmony_ci
43988c2ecf20Sopenharmony_ci	if (info->params.loopback)
43998c2ecf20Sopenharmony_ci		enable_loopback(info);
44008c2ecf20Sopenharmony_ci}
44018c2ecf20Sopenharmony_ci
44028c2ecf20Sopenharmony_ci/*
44038c2ecf20Sopenharmony_ci *  set transmit idle mode
44048c2ecf20Sopenharmony_ci */
44058c2ecf20Sopenharmony_cistatic void tx_set_idle(struct slgt_info *info)
44068c2ecf20Sopenharmony_ci{
44078c2ecf20Sopenharmony_ci	unsigned char val;
44088c2ecf20Sopenharmony_ci	unsigned short tcr;
44098c2ecf20Sopenharmony_ci
44108c2ecf20Sopenharmony_ci	/* if preamble enabled (tcr[6] == 1) then tx idle size = 8 bits
44118c2ecf20Sopenharmony_ci	 * else tcr[5:4] = tx idle size: 00 = 8 bits, 01 = 16 bits
44128c2ecf20Sopenharmony_ci	 */
44138c2ecf20Sopenharmony_ci	tcr = rd_reg16(info, TCR);
44148c2ecf20Sopenharmony_ci	if (info->idle_mode & HDLC_TXIDLE_CUSTOM_16) {
44158c2ecf20Sopenharmony_ci		/* disable preamble, set idle size to 16 bits */
44168c2ecf20Sopenharmony_ci		tcr = (tcr & ~(BIT6 + BIT5)) | BIT4;
44178c2ecf20Sopenharmony_ci		/* MSB of 16 bit idle specified in tx preamble register (TPR) */
44188c2ecf20Sopenharmony_ci		wr_reg8(info, TPR, (unsigned char)((info->idle_mode >> 8) & 0xff));
44198c2ecf20Sopenharmony_ci	} else if (!(tcr & BIT6)) {
44208c2ecf20Sopenharmony_ci		/* preamble is disabled, set idle size to 8 bits */
44218c2ecf20Sopenharmony_ci		tcr &= ~(BIT5 + BIT4);
44228c2ecf20Sopenharmony_ci	}
44238c2ecf20Sopenharmony_ci	wr_reg16(info, TCR, tcr);
44248c2ecf20Sopenharmony_ci
44258c2ecf20Sopenharmony_ci	if (info->idle_mode & (HDLC_TXIDLE_CUSTOM_8 | HDLC_TXIDLE_CUSTOM_16)) {
44268c2ecf20Sopenharmony_ci		/* LSB of custom tx idle specified in tx idle register */
44278c2ecf20Sopenharmony_ci		val = (unsigned char)(info->idle_mode & 0xff);
44288c2ecf20Sopenharmony_ci	} else {
44298c2ecf20Sopenharmony_ci		/* standard 8 bit idle patterns */
44308c2ecf20Sopenharmony_ci		switch(info->idle_mode)
44318c2ecf20Sopenharmony_ci		{
44328c2ecf20Sopenharmony_ci		case HDLC_TXIDLE_FLAGS:          val = 0x7e; break;
44338c2ecf20Sopenharmony_ci		case HDLC_TXIDLE_ALT_ZEROS_ONES:
44348c2ecf20Sopenharmony_ci		case HDLC_TXIDLE_ALT_MARK_SPACE: val = 0xaa; break;
44358c2ecf20Sopenharmony_ci		case HDLC_TXIDLE_ZEROS:
44368c2ecf20Sopenharmony_ci		case HDLC_TXIDLE_SPACE:          val = 0x00; break;
44378c2ecf20Sopenharmony_ci		default:                         val = 0xff;
44388c2ecf20Sopenharmony_ci		}
44398c2ecf20Sopenharmony_ci	}
44408c2ecf20Sopenharmony_ci
44418c2ecf20Sopenharmony_ci	wr_reg8(info, TIR, val);
44428c2ecf20Sopenharmony_ci}
44438c2ecf20Sopenharmony_ci
44448c2ecf20Sopenharmony_ci/*
44458c2ecf20Sopenharmony_ci * get state of V24 status (input) signals
44468c2ecf20Sopenharmony_ci */
44478c2ecf20Sopenharmony_cistatic void get_gtsignals(struct slgt_info *info)
44488c2ecf20Sopenharmony_ci{
44498c2ecf20Sopenharmony_ci	unsigned short status = rd_reg16(info, SSR);
44508c2ecf20Sopenharmony_ci
44518c2ecf20Sopenharmony_ci	/* clear all serial signals except RTS and DTR */
44528c2ecf20Sopenharmony_ci	info->signals &= SerialSignal_RTS | SerialSignal_DTR;
44538c2ecf20Sopenharmony_ci
44548c2ecf20Sopenharmony_ci	if (status & BIT3)
44558c2ecf20Sopenharmony_ci		info->signals |= SerialSignal_DSR;
44568c2ecf20Sopenharmony_ci	if (status & BIT2)
44578c2ecf20Sopenharmony_ci		info->signals |= SerialSignal_CTS;
44588c2ecf20Sopenharmony_ci	if (status & BIT1)
44598c2ecf20Sopenharmony_ci		info->signals |= SerialSignal_DCD;
44608c2ecf20Sopenharmony_ci	if (status & BIT0)
44618c2ecf20Sopenharmony_ci		info->signals |= SerialSignal_RI;
44628c2ecf20Sopenharmony_ci}
44638c2ecf20Sopenharmony_ci
44648c2ecf20Sopenharmony_ci/*
44658c2ecf20Sopenharmony_ci * set V.24 Control Register based on current configuration
44668c2ecf20Sopenharmony_ci */
44678c2ecf20Sopenharmony_cistatic void msc_set_vcr(struct slgt_info *info)
44688c2ecf20Sopenharmony_ci{
44698c2ecf20Sopenharmony_ci	unsigned char val = 0;
44708c2ecf20Sopenharmony_ci
44718c2ecf20Sopenharmony_ci	/* VCR (V.24 control)
44728c2ecf20Sopenharmony_ci	 *
44738c2ecf20Sopenharmony_ci	 * 07..04  serial IF select
44748c2ecf20Sopenharmony_ci	 * 03      DTR
44758c2ecf20Sopenharmony_ci	 * 02      RTS
44768c2ecf20Sopenharmony_ci	 * 01      LL
44778c2ecf20Sopenharmony_ci	 * 00      RL
44788c2ecf20Sopenharmony_ci	 */
44798c2ecf20Sopenharmony_ci
44808c2ecf20Sopenharmony_ci	switch(info->if_mode & MGSL_INTERFACE_MASK)
44818c2ecf20Sopenharmony_ci	{
44828c2ecf20Sopenharmony_ci	case MGSL_INTERFACE_RS232:
44838c2ecf20Sopenharmony_ci		val |= BIT5; /* 0010 */
44848c2ecf20Sopenharmony_ci		break;
44858c2ecf20Sopenharmony_ci	case MGSL_INTERFACE_V35:
44868c2ecf20Sopenharmony_ci		val |= BIT7 + BIT6 + BIT5; /* 1110 */
44878c2ecf20Sopenharmony_ci		break;
44888c2ecf20Sopenharmony_ci	case MGSL_INTERFACE_RS422:
44898c2ecf20Sopenharmony_ci		val |= BIT6; /* 0100 */
44908c2ecf20Sopenharmony_ci		break;
44918c2ecf20Sopenharmony_ci	}
44928c2ecf20Sopenharmony_ci
44938c2ecf20Sopenharmony_ci	if (info->if_mode & MGSL_INTERFACE_MSB_FIRST)
44948c2ecf20Sopenharmony_ci		val |= BIT4;
44958c2ecf20Sopenharmony_ci	if (info->signals & SerialSignal_DTR)
44968c2ecf20Sopenharmony_ci		val |= BIT3;
44978c2ecf20Sopenharmony_ci	if (info->signals & SerialSignal_RTS)
44988c2ecf20Sopenharmony_ci		val |= BIT2;
44998c2ecf20Sopenharmony_ci	if (info->if_mode & MGSL_INTERFACE_LL)
45008c2ecf20Sopenharmony_ci		val |= BIT1;
45018c2ecf20Sopenharmony_ci	if (info->if_mode & MGSL_INTERFACE_RL)
45028c2ecf20Sopenharmony_ci		val |= BIT0;
45038c2ecf20Sopenharmony_ci	wr_reg8(info, VCR, val);
45048c2ecf20Sopenharmony_ci}
45058c2ecf20Sopenharmony_ci
45068c2ecf20Sopenharmony_ci/*
45078c2ecf20Sopenharmony_ci * set state of V24 control (output) signals
45088c2ecf20Sopenharmony_ci */
45098c2ecf20Sopenharmony_cistatic void set_gtsignals(struct slgt_info *info)
45108c2ecf20Sopenharmony_ci{
45118c2ecf20Sopenharmony_ci	unsigned char val = rd_reg8(info, VCR);
45128c2ecf20Sopenharmony_ci	if (info->signals & SerialSignal_DTR)
45138c2ecf20Sopenharmony_ci		val |= BIT3;
45148c2ecf20Sopenharmony_ci	else
45158c2ecf20Sopenharmony_ci		val &= ~BIT3;
45168c2ecf20Sopenharmony_ci	if (info->signals & SerialSignal_RTS)
45178c2ecf20Sopenharmony_ci		val |= BIT2;
45188c2ecf20Sopenharmony_ci	else
45198c2ecf20Sopenharmony_ci		val &= ~BIT2;
45208c2ecf20Sopenharmony_ci	wr_reg8(info, VCR, val);
45218c2ecf20Sopenharmony_ci}
45228c2ecf20Sopenharmony_ci
45238c2ecf20Sopenharmony_ci/*
45248c2ecf20Sopenharmony_ci * free range of receive DMA buffers (i to last)
45258c2ecf20Sopenharmony_ci */
45268c2ecf20Sopenharmony_cistatic void free_rbufs(struct slgt_info *info, unsigned int i, unsigned int last)
45278c2ecf20Sopenharmony_ci{
45288c2ecf20Sopenharmony_ci	int done = 0;
45298c2ecf20Sopenharmony_ci
45308c2ecf20Sopenharmony_ci	while(!done) {
45318c2ecf20Sopenharmony_ci		/* reset current buffer for reuse */
45328c2ecf20Sopenharmony_ci		info->rbufs[i].status = 0;
45338c2ecf20Sopenharmony_ci		set_desc_count(info->rbufs[i], info->rbuf_fill_level);
45348c2ecf20Sopenharmony_ci		if (i == last)
45358c2ecf20Sopenharmony_ci			done = 1;
45368c2ecf20Sopenharmony_ci		if (++i == info->rbuf_count)
45378c2ecf20Sopenharmony_ci			i = 0;
45388c2ecf20Sopenharmony_ci	}
45398c2ecf20Sopenharmony_ci	info->rbuf_current = i;
45408c2ecf20Sopenharmony_ci}
45418c2ecf20Sopenharmony_ci
45428c2ecf20Sopenharmony_ci/*
45438c2ecf20Sopenharmony_ci * mark all receive DMA buffers as free
45448c2ecf20Sopenharmony_ci */
45458c2ecf20Sopenharmony_cistatic void reset_rbufs(struct slgt_info *info)
45468c2ecf20Sopenharmony_ci{
45478c2ecf20Sopenharmony_ci	free_rbufs(info, 0, info->rbuf_count - 1);
45488c2ecf20Sopenharmony_ci	info->rbuf_fill_index = 0;
45498c2ecf20Sopenharmony_ci	info->rbuf_fill_count = 0;
45508c2ecf20Sopenharmony_ci}
45518c2ecf20Sopenharmony_ci
45528c2ecf20Sopenharmony_ci/*
45538c2ecf20Sopenharmony_ci * pass receive HDLC frame to upper layer
45548c2ecf20Sopenharmony_ci *
45558c2ecf20Sopenharmony_ci * return true if frame available, otherwise false
45568c2ecf20Sopenharmony_ci */
45578c2ecf20Sopenharmony_cistatic bool rx_get_frame(struct slgt_info *info)
45588c2ecf20Sopenharmony_ci{
45598c2ecf20Sopenharmony_ci	unsigned int start, end;
45608c2ecf20Sopenharmony_ci	unsigned short status;
45618c2ecf20Sopenharmony_ci	unsigned int framesize = 0;
45628c2ecf20Sopenharmony_ci	unsigned long flags;
45638c2ecf20Sopenharmony_ci	struct tty_struct *tty = info->port.tty;
45648c2ecf20Sopenharmony_ci	unsigned char addr_field = 0xff;
45658c2ecf20Sopenharmony_ci	unsigned int crc_size = 0;
45668c2ecf20Sopenharmony_ci
45678c2ecf20Sopenharmony_ci	switch (info->params.crc_type & HDLC_CRC_MASK) {
45688c2ecf20Sopenharmony_ci	case HDLC_CRC_16_CCITT: crc_size = 2; break;
45698c2ecf20Sopenharmony_ci	case HDLC_CRC_32_CCITT: crc_size = 4; break;
45708c2ecf20Sopenharmony_ci	}
45718c2ecf20Sopenharmony_ci
45728c2ecf20Sopenharmony_cicheck_again:
45738c2ecf20Sopenharmony_ci
45748c2ecf20Sopenharmony_ci	framesize = 0;
45758c2ecf20Sopenharmony_ci	addr_field = 0xff;
45768c2ecf20Sopenharmony_ci	start = end = info->rbuf_current;
45778c2ecf20Sopenharmony_ci
45788c2ecf20Sopenharmony_ci	for (;;) {
45798c2ecf20Sopenharmony_ci		if (!desc_complete(info->rbufs[end]))
45808c2ecf20Sopenharmony_ci			goto cleanup;
45818c2ecf20Sopenharmony_ci
45828c2ecf20Sopenharmony_ci		if (framesize == 0 && info->params.addr_filter != 0xff)
45838c2ecf20Sopenharmony_ci			addr_field = info->rbufs[end].buf[0];
45848c2ecf20Sopenharmony_ci
45858c2ecf20Sopenharmony_ci		framesize += desc_count(info->rbufs[end]);
45868c2ecf20Sopenharmony_ci
45878c2ecf20Sopenharmony_ci		if (desc_eof(info->rbufs[end]))
45888c2ecf20Sopenharmony_ci			break;
45898c2ecf20Sopenharmony_ci
45908c2ecf20Sopenharmony_ci		if (++end == info->rbuf_count)
45918c2ecf20Sopenharmony_ci			end = 0;
45928c2ecf20Sopenharmony_ci
45938c2ecf20Sopenharmony_ci		if (end == info->rbuf_current) {
45948c2ecf20Sopenharmony_ci			if (info->rx_enabled){
45958c2ecf20Sopenharmony_ci				spin_lock_irqsave(&info->lock,flags);
45968c2ecf20Sopenharmony_ci				rx_start(info);
45978c2ecf20Sopenharmony_ci				spin_unlock_irqrestore(&info->lock,flags);
45988c2ecf20Sopenharmony_ci			}
45998c2ecf20Sopenharmony_ci			goto cleanup;
46008c2ecf20Sopenharmony_ci		}
46018c2ecf20Sopenharmony_ci	}
46028c2ecf20Sopenharmony_ci
46038c2ecf20Sopenharmony_ci	/* status
46048c2ecf20Sopenharmony_ci	 *
46058c2ecf20Sopenharmony_ci	 * 15      buffer complete
46068c2ecf20Sopenharmony_ci	 * 14..06  reserved
46078c2ecf20Sopenharmony_ci	 * 05..04  residue
46088c2ecf20Sopenharmony_ci	 * 02      eof (end of frame)
46098c2ecf20Sopenharmony_ci	 * 01      CRC error
46108c2ecf20Sopenharmony_ci	 * 00      abort
46118c2ecf20Sopenharmony_ci	 */
46128c2ecf20Sopenharmony_ci	status = desc_status(info->rbufs[end]);
46138c2ecf20Sopenharmony_ci
46148c2ecf20Sopenharmony_ci	/* ignore CRC bit if not using CRC (bit is undefined) */
46158c2ecf20Sopenharmony_ci	if ((info->params.crc_type & HDLC_CRC_MASK) == HDLC_CRC_NONE)
46168c2ecf20Sopenharmony_ci		status &= ~BIT1;
46178c2ecf20Sopenharmony_ci
46188c2ecf20Sopenharmony_ci	if (framesize == 0 ||
46198c2ecf20Sopenharmony_ci		 (addr_field != 0xff && addr_field != info->params.addr_filter)) {
46208c2ecf20Sopenharmony_ci		free_rbufs(info, start, end);
46218c2ecf20Sopenharmony_ci		goto check_again;
46228c2ecf20Sopenharmony_ci	}
46238c2ecf20Sopenharmony_ci
46248c2ecf20Sopenharmony_ci	if (framesize < (2 + crc_size) || status & BIT0) {
46258c2ecf20Sopenharmony_ci		info->icount.rxshort++;
46268c2ecf20Sopenharmony_ci		framesize = 0;
46278c2ecf20Sopenharmony_ci	} else if (status & BIT1) {
46288c2ecf20Sopenharmony_ci		info->icount.rxcrc++;
46298c2ecf20Sopenharmony_ci		if (!(info->params.crc_type & HDLC_CRC_RETURN_EX))
46308c2ecf20Sopenharmony_ci			framesize = 0;
46318c2ecf20Sopenharmony_ci	}
46328c2ecf20Sopenharmony_ci
46338c2ecf20Sopenharmony_ci#if SYNCLINK_GENERIC_HDLC
46348c2ecf20Sopenharmony_ci	if (framesize == 0) {
46358c2ecf20Sopenharmony_ci		info->netdev->stats.rx_errors++;
46368c2ecf20Sopenharmony_ci		info->netdev->stats.rx_frame_errors++;
46378c2ecf20Sopenharmony_ci	}
46388c2ecf20Sopenharmony_ci#endif
46398c2ecf20Sopenharmony_ci
46408c2ecf20Sopenharmony_ci	DBGBH(("%s rx frame status=%04X size=%d\n",
46418c2ecf20Sopenharmony_ci		info->device_name, status, framesize));
46428c2ecf20Sopenharmony_ci	DBGDATA(info, info->rbufs[start].buf, min_t(int, framesize, info->rbuf_fill_level), "rx");
46438c2ecf20Sopenharmony_ci
46448c2ecf20Sopenharmony_ci	if (framesize) {
46458c2ecf20Sopenharmony_ci		if (!(info->params.crc_type & HDLC_CRC_RETURN_EX)) {
46468c2ecf20Sopenharmony_ci			framesize -= crc_size;
46478c2ecf20Sopenharmony_ci			crc_size = 0;
46488c2ecf20Sopenharmony_ci		}
46498c2ecf20Sopenharmony_ci
46508c2ecf20Sopenharmony_ci		if (framesize > info->max_frame_size + crc_size)
46518c2ecf20Sopenharmony_ci			info->icount.rxlong++;
46528c2ecf20Sopenharmony_ci		else {
46538c2ecf20Sopenharmony_ci			/* copy dma buffer(s) to contiguous temp buffer */
46548c2ecf20Sopenharmony_ci			int copy_count = framesize;
46558c2ecf20Sopenharmony_ci			int i = start;
46568c2ecf20Sopenharmony_ci			unsigned char *p = info->tmp_rbuf;
46578c2ecf20Sopenharmony_ci			info->tmp_rbuf_count = framesize;
46588c2ecf20Sopenharmony_ci
46598c2ecf20Sopenharmony_ci			info->icount.rxok++;
46608c2ecf20Sopenharmony_ci
46618c2ecf20Sopenharmony_ci			while(copy_count) {
46628c2ecf20Sopenharmony_ci				int partial_count = min_t(int, copy_count, info->rbuf_fill_level);
46638c2ecf20Sopenharmony_ci				memcpy(p, info->rbufs[i].buf, partial_count);
46648c2ecf20Sopenharmony_ci				p += partial_count;
46658c2ecf20Sopenharmony_ci				copy_count -= partial_count;
46668c2ecf20Sopenharmony_ci				if (++i == info->rbuf_count)
46678c2ecf20Sopenharmony_ci					i = 0;
46688c2ecf20Sopenharmony_ci			}
46698c2ecf20Sopenharmony_ci
46708c2ecf20Sopenharmony_ci			if (info->params.crc_type & HDLC_CRC_RETURN_EX) {
46718c2ecf20Sopenharmony_ci				*p = (status & BIT1) ? RX_CRC_ERROR : RX_OK;
46728c2ecf20Sopenharmony_ci				framesize++;
46738c2ecf20Sopenharmony_ci			}
46748c2ecf20Sopenharmony_ci
46758c2ecf20Sopenharmony_ci#if SYNCLINK_GENERIC_HDLC
46768c2ecf20Sopenharmony_ci			if (info->netcount)
46778c2ecf20Sopenharmony_ci				hdlcdev_rx(info,info->tmp_rbuf, framesize);
46788c2ecf20Sopenharmony_ci			else
46798c2ecf20Sopenharmony_ci#endif
46808c2ecf20Sopenharmony_ci				ldisc_receive_buf(tty, info->tmp_rbuf, info->flag_buf, framesize);
46818c2ecf20Sopenharmony_ci		}
46828c2ecf20Sopenharmony_ci	}
46838c2ecf20Sopenharmony_ci	free_rbufs(info, start, end);
46848c2ecf20Sopenharmony_ci	return true;
46858c2ecf20Sopenharmony_ci
46868c2ecf20Sopenharmony_cicleanup:
46878c2ecf20Sopenharmony_ci	return false;
46888c2ecf20Sopenharmony_ci}
46898c2ecf20Sopenharmony_ci
46908c2ecf20Sopenharmony_ci/*
46918c2ecf20Sopenharmony_ci * pass receive buffer (RAW synchronous mode) to tty layer
46928c2ecf20Sopenharmony_ci * return true if buffer available, otherwise false
46938c2ecf20Sopenharmony_ci */
46948c2ecf20Sopenharmony_cistatic bool rx_get_buf(struct slgt_info *info)
46958c2ecf20Sopenharmony_ci{
46968c2ecf20Sopenharmony_ci	unsigned int i = info->rbuf_current;
46978c2ecf20Sopenharmony_ci	unsigned int count;
46988c2ecf20Sopenharmony_ci
46998c2ecf20Sopenharmony_ci	if (!desc_complete(info->rbufs[i]))
47008c2ecf20Sopenharmony_ci		return false;
47018c2ecf20Sopenharmony_ci	count = desc_count(info->rbufs[i]);
47028c2ecf20Sopenharmony_ci	switch(info->params.mode) {
47038c2ecf20Sopenharmony_ci	case MGSL_MODE_MONOSYNC:
47048c2ecf20Sopenharmony_ci	case MGSL_MODE_BISYNC:
47058c2ecf20Sopenharmony_ci	case MGSL_MODE_XSYNC:
47068c2ecf20Sopenharmony_ci		/* ignore residue in byte synchronous modes */
47078c2ecf20Sopenharmony_ci		if (desc_residue(info->rbufs[i]))
47088c2ecf20Sopenharmony_ci			count--;
47098c2ecf20Sopenharmony_ci		break;
47108c2ecf20Sopenharmony_ci	}
47118c2ecf20Sopenharmony_ci	DBGDATA(info, info->rbufs[i].buf, count, "rx");
47128c2ecf20Sopenharmony_ci	DBGINFO(("rx_get_buf size=%d\n", count));
47138c2ecf20Sopenharmony_ci	if (count)
47148c2ecf20Sopenharmony_ci		ldisc_receive_buf(info->port.tty, info->rbufs[i].buf,
47158c2ecf20Sopenharmony_ci				  info->flag_buf, count);
47168c2ecf20Sopenharmony_ci	free_rbufs(info, i, i);
47178c2ecf20Sopenharmony_ci	return true;
47188c2ecf20Sopenharmony_ci}
47198c2ecf20Sopenharmony_ci
47208c2ecf20Sopenharmony_cistatic void reset_tbufs(struct slgt_info *info)
47218c2ecf20Sopenharmony_ci{
47228c2ecf20Sopenharmony_ci	unsigned int i;
47238c2ecf20Sopenharmony_ci	info->tbuf_current = 0;
47248c2ecf20Sopenharmony_ci	for (i=0 ; i < info->tbuf_count ; i++) {
47258c2ecf20Sopenharmony_ci		info->tbufs[i].status = 0;
47268c2ecf20Sopenharmony_ci		info->tbufs[i].count  = 0;
47278c2ecf20Sopenharmony_ci	}
47288c2ecf20Sopenharmony_ci}
47298c2ecf20Sopenharmony_ci
47308c2ecf20Sopenharmony_ci/*
47318c2ecf20Sopenharmony_ci * return number of free transmit DMA buffers
47328c2ecf20Sopenharmony_ci */
47338c2ecf20Sopenharmony_cistatic unsigned int free_tbuf_count(struct slgt_info *info)
47348c2ecf20Sopenharmony_ci{
47358c2ecf20Sopenharmony_ci	unsigned int count = 0;
47368c2ecf20Sopenharmony_ci	unsigned int i = info->tbuf_current;
47378c2ecf20Sopenharmony_ci
47388c2ecf20Sopenharmony_ci	do
47398c2ecf20Sopenharmony_ci	{
47408c2ecf20Sopenharmony_ci		if (desc_count(info->tbufs[i]))
47418c2ecf20Sopenharmony_ci			break; /* buffer in use */
47428c2ecf20Sopenharmony_ci		++count;
47438c2ecf20Sopenharmony_ci		if (++i == info->tbuf_count)
47448c2ecf20Sopenharmony_ci			i=0;
47458c2ecf20Sopenharmony_ci	} while (i != info->tbuf_current);
47468c2ecf20Sopenharmony_ci
47478c2ecf20Sopenharmony_ci	/* if tx DMA active, last zero count buffer is in use */
47488c2ecf20Sopenharmony_ci	if (count && (rd_reg32(info, TDCSR) & BIT0))
47498c2ecf20Sopenharmony_ci		--count;
47508c2ecf20Sopenharmony_ci
47518c2ecf20Sopenharmony_ci	return count;
47528c2ecf20Sopenharmony_ci}
47538c2ecf20Sopenharmony_ci
47548c2ecf20Sopenharmony_ci/*
47558c2ecf20Sopenharmony_ci * return number of bytes in unsent transmit DMA buffers
47568c2ecf20Sopenharmony_ci * and the serial controller tx FIFO
47578c2ecf20Sopenharmony_ci */
47588c2ecf20Sopenharmony_cistatic unsigned int tbuf_bytes(struct slgt_info *info)
47598c2ecf20Sopenharmony_ci{
47608c2ecf20Sopenharmony_ci	unsigned int total_count = 0;
47618c2ecf20Sopenharmony_ci	unsigned int i = info->tbuf_current;
47628c2ecf20Sopenharmony_ci	unsigned int reg_value;
47638c2ecf20Sopenharmony_ci	unsigned int count;
47648c2ecf20Sopenharmony_ci	unsigned int active_buf_count = 0;
47658c2ecf20Sopenharmony_ci
47668c2ecf20Sopenharmony_ci	/*
47678c2ecf20Sopenharmony_ci	 * Add descriptor counts for all tx DMA buffers.
47688c2ecf20Sopenharmony_ci	 * If count is zero (cleared by DMA controller after read),
47698c2ecf20Sopenharmony_ci	 * the buffer is complete or is actively being read from.
47708c2ecf20Sopenharmony_ci	 *
47718c2ecf20Sopenharmony_ci	 * Record buf_count of last buffer with zero count starting
47728c2ecf20Sopenharmony_ci	 * from current ring position. buf_count is mirror
47738c2ecf20Sopenharmony_ci	 * copy of count and is not cleared by serial controller.
47748c2ecf20Sopenharmony_ci	 * If DMA controller is active, that buffer is actively
47758c2ecf20Sopenharmony_ci	 * being read so add to total.
47768c2ecf20Sopenharmony_ci	 */
47778c2ecf20Sopenharmony_ci	do {
47788c2ecf20Sopenharmony_ci		count = desc_count(info->tbufs[i]);
47798c2ecf20Sopenharmony_ci		if (count)
47808c2ecf20Sopenharmony_ci			total_count += count;
47818c2ecf20Sopenharmony_ci		else if (!total_count)
47828c2ecf20Sopenharmony_ci			active_buf_count = info->tbufs[i].buf_count;
47838c2ecf20Sopenharmony_ci		if (++i == info->tbuf_count)
47848c2ecf20Sopenharmony_ci			i = 0;
47858c2ecf20Sopenharmony_ci	} while (i != info->tbuf_current);
47868c2ecf20Sopenharmony_ci
47878c2ecf20Sopenharmony_ci	/* read tx DMA status register */
47888c2ecf20Sopenharmony_ci	reg_value = rd_reg32(info, TDCSR);
47898c2ecf20Sopenharmony_ci
47908c2ecf20Sopenharmony_ci	/* if tx DMA active, last zero count buffer is in use */
47918c2ecf20Sopenharmony_ci	if (reg_value & BIT0)
47928c2ecf20Sopenharmony_ci		total_count += active_buf_count;
47938c2ecf20Sopenharmony_ci
47948c2ecf20Sopenharmony_ci	/* add tx FIFO count = reg_value[15..8] */
47958c2ecf20Sopenharmony_ci	total_count += (reg_value >> 8) & 0xff;
47968c2ecf20Sopenharmony_ci
47978c2ecf20Sopenharmony_ci	/* if transmitter active add one byte for shift register */
47988c2ecf20Sopenharmony_ci	if (info->tx_active)
47998c2ecf20Sopenharmony_ci		total_count++;
48008c2ecf20Sopenharmony_ci
48018c2ecf20Sopenharmony_ci	return total_count;
48028c2ecf20Sopenharmony_ci}
48038c2ecf20Sopenharmony_ci
48048c2ecf20Sopenharmony_ci/*
48058c2ecf20Sopenharmony_ci * load data into transmit DMA buffer ring and start transmitter if needed
48068c2ecf20Sopenharmony_ci * return true if data accepted, otherwise false (buffers full)
48078c2ecf20Sopenharmony_ci */
48088c2ecf20Sopenharmony_cistatic bool tx_load(struct slgt_info *info, const char *buf, unsigned int size)
48098c2ecf20Sopenharmony_ci{
48108c2ecf20Sopenharmony_ci	unsigned short count;
48118c2ecf20Sopenharmony_ci	unsigned int i;
48128c2ecf20Sopenharmony_ci	struct slgt_desc *d;
48138c2ecf20Sopenharmony_ci
48148c2ecf20Sopenharmony_ci	/* check required buffer space */
48158c2ecf20Sopenharmony_ci	if (DIV_ROUND_UP(size, DMABUFSIZE) > free_tbuf_count(info))
48168c2ecf20Sopenharmony_ci		return false;
48178c2ecf20Sopenharmony_ci
48188c2ecf20Sopenharmony_ci	DBGDATA(info, buf, size, "tx");
48198c2ecf20Sopenharmony_ci
48208c2ecf20Sopenharmony_ci	/*
48218c2ecf20Sopenharmony_ci	 * copy data to one or more DMA buffers in circular ring
48228c2ecf20Sopenharmony_ci	 * tbuf_start   = first buffer for this data
48238c2ecf20Sopenharmony_ci	 * tbuf_current = next free buffer
48248c2ecf20Sopenharmony_ci	 *
48258c2ecf20Sopenharmony_ci	 * Copy all data before making data visible to DMA controller by
48268c2ecf20Sopenharmony_ci	 * setting descriptor count of the first buffer.
48278c2ecf20Sopenharmony_ci	 * This prevents an active DMA controller from reading the first DMA
48288c2ecf20Sopenharmony_ci	 * buffers of a frame and stopping before the final buffers are filled.
48298c2ecf20Sopenharmony_ci	 */
48308c2ecf20Sopenharmony_ci
48318c2ecf20Sopenharmony_ci	info->tbuf_start = i = info->tbuf_current;
48328c2ecf20Sopenharmony_ci
48338c2ecf20Sopenharmony_ci	while (size) {
48348c2ecf20Sopenharmony_ci		d = &info->tbufs[i];
48358c2ecf20Sopenharmony_ci
48368c2ecf20Sopenharmony_ci		count = (unsigned short)((size > DMABUFSIZE) ? DMABUFSIZE : size);
48378c2ecf20Sopenharmony_ci		memcpy(d->buf, buf, count);
48388c2ecf20Sopenharmony_ci
48398c2ecf20Sopenharmony_ci		size -= count;
48408c2ecf20Sopenharmony_ci		buf  += count;
48418c2ecf20Sopenharmony_ci
48428c2ecf20Sopenharmony_ci		/*
48438c2ecf20Sopenharmony_ci		 * set EOF bit for last buffer of HDLC frame or
48448c2ecf20Sopenharmony_ci		 * for every buffer in raw mode
48458c2ecf20Sopenharmony_ci		 */
48468c2ecf20Sopenharmony_ci		if ((!size && info->params.mode == MGSL_MODE_HDLC) ||
48478c2ecf20Sopenharmony_ci		    info->params.mode == MGSL_MODE_RAW)
48488c2ecf20Sopenharmony_ci			set_desc_eof(*d, 1);
48498c2ecf20Sopenharmony_ci		else
48508c2ecf20Sopenharmony_ci			set_desc_eof(*d, 0);
48518c2ecf20Sopenharmony_ci
48528c2ecf20Sopenharmony_ci		/* set descriptor count for all but first buffer */
48538c2ecf20Sopenharmony_ci		if (i != info->tbuf_start)
48548c2ecf20Sopenharmony_ci			set_desc_count(*d, count);
48558c2ecf20Sopenharmony_ci		d->buf_count = count;
48568c2ecf20Sopenharmony_ci
48578c2ecf20Sopenharmony_ci		if (++i == info->tbuf_count)
48588c2ecf20Sopenharmony_ci			i = 0;
48598c2ecf20Sopenharmony_ci	}
48608c2ecf20Sopenharmony_ci
48618c2ecf20Sopenharmony_ci	info->tbuf_current = i;
48628c2ecf20Sopenharmony_ci
48638c2ecf20Sopenharmony_ci	/* set first buffer count to make new data visible to DMA controller */
48648c2ecf20Sopenharmony_ci	d = &info->tbufs[info->tbuf_start];
48658c2ecf20Sopenharmony_ci	set_desc_count(*d, d->buf_count);
48668c2ecf20Sopenharmony_ci
48678c2ecf20Sopenharmony_ci	/* start transmitter if needed and update transmit timeout */
48688c2ecf20Sopenharmony_ci	if (!info->tx_active)
48698c2ecf20Sopenharmony_ci		tx_start(info);
48708c2ecf20Sopenharmony_ci	update_tx_timer(info);
48718c2ecf20Sopenharmony_ci
48728c2ecf20Sopenharmony_ci	return true;
48738c2ecf20Sopenharmony_ci}
48748c2ecf20Sopenharmony_ci
48758c2ecf20Sopenharmony_cistatic int register_test(struct slgt_info *info)
48768c2ecf20Sopenharmony_ci{
48778c2ecf20Sopenharmony_ci	static unsigned short patterns[] =
48788c2ecf20Sopenharmony_ci		{0x0000, 0xffff, 0xaaaa, 0x5555, 0x6969, 0x9696};
48798c2ecf20Sopenharmony_ci	static unsigned int count = ARRAY_SIZE(patterns);
48808c2ecf20Sopenharmony_ci	unsigned int i;
48818c2ecf20Sopenharmony_ci	int rc = 0;
48828c2ecf20Sopenharmony_ci
48838c2ecf20Sopenharmony_ci	for (i=0 ; i < count ; i++) {
48848c2ecf20Sopenharmony_ci		wr_reg16(info, TIR, patterns[i]);
48858c2ecf20Sopenharmony_ci		wr_reg16(info, BDR, patterns[(i+1)%count]);
48868c2ecf20Sopenharmony_ci		if ((rd_reg16(info, TIR) != patterns[i]) ||
48878c2ecf20Sopenharmony_ci		    (rd_reg16(info, BDR) != patterns[(i+1)%count])) {
48888c2ecf20Sopenharmony_ci			rc = -ENODEV;
48898c2ecf20Sopenharmony_ci			break;
48908c2ecf20Sopenharmony_ci		}
48918c2ecf20Sopenharmony_ci	}
48928c2ecf20Sopenharmony_ci	info->gpio_present = (rd_reg32(info, JCR) & BIT5) ? 1 : 0;
48938c2ecf20Sopenharmony_ci	info->init_error = rc ? 0 : DiagStatus_AddressFailure;
48948c2ecf20Sopenharmony_ci	return rc;
48958c2ecf20Sopenharmony_ci}
48968c2ecf20Sopenharmony_ci
48978c2ecf20Sopenharmony_cistatic int irq_test(struct slgt_info *info)
48988c2ecf20Sopenharmony_ci{
48998c2ecf20Sopenharmony_ci	unsigned long timeout;
49008c2ecf20Sopenharmony_ci	unsigned long flags;
49018c2ecf20Sopenharmony_ci	struct tty_struct *oldtty = info->port.tty;
49028c2ecf20Sopenharmony_ci	u32 speed = info->params.data_rate;
49038c2ecf20Sopenharmony_ci
49048c2ecf20Sopenharmony_ci	info->params.data_rate = 921600;
49058c2ecf20Sopenharmony_ci	info->port.tty = NULL;
49068c2ecf20Sopenharmony_ci
49078c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->lock, flags);
49088c2ecf20Sopenharmony_ci	async_mode(info);
49098c2ecf20Sopenharmony_ci	slgt_irq_on(info, IRQ_TXIDLE);
49108c2ecf20Sopenharmony_ci
49118c2ecf20Sopenharmony_ci	/* enable transmitter */
49128c2ecf20Sopenharmony_ci	wr_reg16(info, TCR,
49138c2ecf20Sopenharmony_ci		(unsigned short)(rd_reg16(info, TCR) | BIT1));
49148c2ecf20Sopenharmony_ci
49158c2ecf20Sopenharmony_ci	/* write one byte and wait for tx idle */
49168c2ecf20Sopenharmony_ci	wr_reg16(info, TDR, 0);
49178c2ecf20Sopenharmony_ci
49188c2ecf20Sopenharmony_ci	/* assume failure */
49198c2ecf20Sopenharmony_ci	info->init_error = DiagStatus_IrqFailure;
49208c2ecf20Sopenharmony_ci	info->irq_occurred = false;
49218c2ecf20Sopenharmony_ci
49228c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->lock, flags);
49238c2ecf20Sopenharmony_ci
49248c2ecf20Sopenharmony_ci	timeout=100;
49258c2ecf20Sopenharmony_ci	while(timeout-- && !info->irq_occurred)
49268c2ecf20Sopenharmony_ci		msleep_interruptible(10);
49278c2ecf20Sopenharmony_ci
49288c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
49298c2ecf20Sopenharmony_ci	reset_port(info);
49308c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
49318c2ecf20Sopenharmony_ci
49328c2ecf20Sopenharmony_ci	info->params.data_rate = speed;
49338c2ecf20Sopenharmony_ci	info->port.tty = oldtty;
49348c2ecf20Sopenharmony_ci
49358c2ecf20Sopenharmony_ci	info->init_error = info->irq_occurred ? 0 : DiagStatus_IrqFailure;
49368c2ecf20Sopenharmony_ci	return info->irq_occurred ? 0 : -ENODEV;
49378c2ecf20Sopenharmony_ci}
49388c2ecf20Sopenharmony_ci
49398c2ecf20Sopenharmony_cistatic int loopback_test_rx(struct slgt_info *info)
49408c2ecf20Sopenharmony_ci{
49418c2ecf20Sopenharmony_ci	unsigned char *src, *dest;
49428c2ecf20Sopenharmony_ci	int count;
49438c2ecf20Sopenharmony_ci
49448c2ecf20Sopenharmony_ci	if (desc_complete(info->rbufs[0])) {
49458c2ecf20Sopenharmony_ci		count = desc_count(info->rbufs[0]);
49468c2ecf20Sopenharmony_ci		src   = info->rbufs[0].buf;
49478c2ecf20Sopenharmony_ci		dest  = info->tmp_rbuf;
49488c2ecf20Sopenharmony_ci
49498c2ecf20Sopenharmony_ci		for( ; count ; count-=2, src+=2) {
49508c2ecf20Sopenharmony_ci			/* src=data byte (src+1)=status byte */
49518c2ecf20Sopenharmony_ci			if (!(*(src+1) & (BIT9 + BIT8))) {
49528c2ecf20Sopenharmony_ci				*dest = *src;
49538c2ecf20Sopenharmony_ci				dest++;
49548c2ecf20Sopenharmony_ci				info->tmp_rbuf_count++;
49558c2ecf20Sopenharmony_ci			}
49568c2ecf20Sopenharmony_ci		}
49578c2ecf20Sopenharmony_ci		DBGDATA(info, info->tmp_rbuf, info->tmp_rbuf_count, "rx");
49588c2ecf20Sopenharmony_ci		return 1;
49598c2ecf20Sopenharmony_ci	}
49608c2ecf20Sopenharmony_ci	return 0;
49618c2ecf20Sopenharmony_ci}
49628c2ecf20Sopenharmony_ci
49638c2ecf20Sopenharmony_cistatic int loopback_test(struct slgt_info *info)
49648c2ecf20Sopenharmony_ci{
49658c2ecf20Sopenharmony_ci#define TESTFRAMESIZE 20
49668c2ecf20Sopenharmony_ci
49678c2ecf20Sopenharmony_ci	unsigned long timeout;
49688c2ecf20Sopenharmony_ci	u16 count = TESTFRAMESIZE;
49698c2ecf20Sopenharmony_ci	unsigned char buf[TESTFRAMESIZE];
49708c2ecf20Sopenharmony_ci	int rc = -ENODEV;
49718c2ecf20Sopenharmony_ci	unsigned long flags;
49728c2ecf20Sopenharmony_ci
49738c2ecf20Sopenharmony_ci	struct tty_struct *oldtty = info->port.tty;
49748c2ecf20Sopenharmony_ci	MGSL_PARAMS params;
49758c2ecf20Sopenharmony_ci
49768c2ecf20Sopenharmony_ci	memcpy(&params, &info->params, sizeof(params));
49778c2ecf20Sopenharmony_ci
49788c2ecf20Sopenharmony_ci	info->params.mode = MGSL_MODE_ASYNC;
49798c2ecf20Sopenharmony_ci	info->params.data_rate = 921600;
49808c2ecf20Sopenharmony_ci	info->params.loopback = 1;
49818c2ecf20Sopenharmony_ci	info->port.tty = NULL;
49828c2ecf20Sopenharmony_ci
49838c2ecf20Sopenharmony_ci	/* build and send transmit frame */
49848c2ecf20Sopenharmony_ci	for (count = 0; count < TESTFRAMESIZE; ++count)
49858c2ecf20Sopenharmony_ci		buf[count] = (unsigned char)count;
49868c2ecf20Sopenharmony_ci
49878c2ecf20Sopenharmony_ci	info->tmp_rbuf_count = 0;
49888c2ecf20Sopenharmony_ci	memset(info->tmp_rbuf, 0, TESTFRAMESIZE);
49898c2ecf20Sopenharmony_ci
49908c2ecf20Sopenharmony_ci	/* program hardware for HDLC and enabled receiver */
49918c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
49928c2ecf20Sopenharmony_ci	async_mode(info);
49938c2ecf20Sopenharmony_ci	rx_start(info);
49948c2ecf20Sopenharmony_ci	tx_load(info, buf, count);
49958c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->lock, flags);
49968c2ecf20Sopenharmony_ci
49978c2ecf20Sopenharmony_ci	/* wait for receive complete */
49988c2ecf20Sopenharmony_ci	for (timeout = 100; timeout; --timeout) {
49998c2ecf20Sopenharmony_ci		msleep_interruptible(10);
50008c2ecf20Sopenharmony_ci		if (loopback_test_rx(info)) {
50018c2ecf20Sopenharmony_ci			rc = 0;
50028c2ecf20Sopenharmony_ci			break;
50038c2ecf20Sopenharmony_ci		}
50048c2ecf20Sopenharmony_ci	}
50058c2ecf20Sopenharmony_ci
50068c2ecf20Sopenharmony_ci	/* verify received frame length and contents */
50078c2ecf20Sopenharmony_ci	if (!rc && (info->tmp_rbuf_count != count ||
50088c2ecf20Sopenharmony_ci		  memcmp(buf, info->tmp_rbuf, count))) {
50098c2ecf20Sopenharmony_ci		rc = -ENODEV;
50108c2ecf20Sopenharmony_ci	}
50118c2ecf20Sopenharmony_ci
50128c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
50138c2ecf20Sopenharmony_ci	reset_adapter(info);
50148c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
50158c2ecf20Sopenharmony_ci
50168c2ecf20Sopenharmony_ci	memcpy(&info->params, &params, sizeof(info->params));
50178c2ecf20Sopenharmony_ci	info->port.tty = oldtty;
50188c2ecf20Sopenharmony_ci
50198c2ecf20Sopenharmony_ci	info->init_error = rc ? DiagStatus_DmaFailure : 0;
50208c2ecf20Sopenharmony_ci	return rc;
50218c2ecf20Sopenharmony_ci}
50228c2ecf20Sopenharmony_ci
50238c2ecf20Sopenharmony_cistatic int adapter_test(struct slgt_info *info)
50248c2ecf20Sopenharmony_ci{
50258c2ecf20Sopenharmony_ci	DBGINFO(("testing %s\n", info->device_name));
50268c2ecf20Sopenharmony_ci	if (register_test(info) < 0) {
50278c2ecf20Sopenharmony_ci		printk("register test failure %s addr=%08X\n",
50288c2ecf20Sopenharmony_ci			info->device_name, info->phys_reg_addr);
50298c2ecf20Sopenharmony_ci	} else if (irq_test(info) < 0) {
50308c2ecf20Sopenharmony_ci		printk("IRQ test failure %s IRQ=%d\n",
50318c2ecf20Sopenharmony_ci			info->device_name, info->irq_level);
50328c2ecf20Sopenharmony_ci	} else if (loopback_test(info) < 0) {
50338c2ecf20Sopenharmony_ci		printk("loopback test failure %s\n", info->device_name);
50348c2ecf20Sopenharmony_ci	}
50358c2ecf20Sopenharmony_ci	return info->init_error;
50368c2ecf20Sopenharmony_ci}
50378c2ecf20Sopenharmony_ci
50388c2ecf20Sopenharmony_ci/*
50398c2ecf20Sopenharmony_ci * transmit timeout handler
50408c2ecf20Sopenharmony_ci */
50418c2ecf20Sopenharmony_cistatic void tx_timeout(struct timer_list *t)
50428c2ecf20Sopenharmony_ci{
50438c2ecf20Sopenharmony_ci	struct slgt_info *info = from_timer(info, t, tx_timer);
50448c2ecf20Sopenharmony_ci	unsigned long flags;
50458c2ecf20Sopenharmony_ci
50468c2ecf20Sopenharmony_ci	DBGINFO(("%s tx_timeout\n", info->device_name));
50478c2ecf20Sopenharmony_ci	if(info->tx_active && info->params.mode == MGSL_MODE_HDLC) {
50488c2ecf20Sopenharmony_ci		info->icount.txtimeout++;
50498c2ecf20Sopenharmony_ci	}
50508c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->lock,flags);
50518c2ecf20Sopenharmony_ci	tx_stop(info);
50528c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->lock,flags);
50538c2ecf20Sopenharmony_ci
50548c2ecf20Sopenharmony_ci#if SYNCLINK_GENERIC_HDLC
50558c2ecf20Sopenharmony_ci	if (info->netcount)
50568c2ecf20Sopenharmony_ci		hdlcdev_tx_done(info);
50578c2ecf20Sopenharmony_ci	else
50588c2ecf20Sopenharmony_ci#endif
50598c2ecf20Sopenharmony_ci		bh_transmit(info);
50608c2ecf20Sopenharmony_ci}
50618c2ecf20Sopenharmony_ci
50628c2ecf20Sopenharmony_ci/*
50638c2ecf20Sopenharmony_ci * receive buffer polling timer
50648c2ecf20Sopenharmony_ci */
50658c2ecf20Sopenharmony_cistatic void rx_timeout(struct timer_list *t)
50668c2ecf20Sopenharmony_ci{
50678c2ecf20Sopenharmony_ci	struct slgt_info *info = from_timer(info, t, rx_timer);
50688c2ecf20Sopenharmony_ci	unsigned long flags;
50698c2ecf20Sopenharmony_ci
50708c2ecf20Sopenharmony_ci	DBGINFO(("%s rx_timeout\n", info->device_name));
50718c2ecf20Sopenharmony_ci	spin_lock_irqsave(&info->lock, flags);
50728c2ecf20Sopenharmony_ci	info->pending_bh |= BH_RECEIVE;
50738c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&info->lock, flags);
50748c2ecf20Sopenharmony_ci	bh_handler(&info->task);
50758c2ecf20Sopenharmony_ci}
50768c2ecf20Sopenharmony_ci
5077