162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Clean ups from Moschip version and a few ioctl implementations by:
462306a36Sopenharmony_ci *	Paul B Schroeder <pschroeder "at" uplogix "dot" com>
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Originally based on drivers/usb/serial/io_edgeport.c which is:
762306a36Sopenharmony_ci *      Copyright (C) 2000 Inside Out Networks, All rights reserved.
862306a36Sopenharmony_ci *      Copyright (C) 2001-2002 Greg Kroah-Hartman <greg@kroah.com>
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/errno.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci#include <linux/tty.h>
1662306a36Sopenharmony_ci#include <linux/tty_driver.h>
1762306a36Sopenharmony_ci#include <linux/tty_flip.h>
1862306a36Sopenharmony_ci#include <linux/module.h>
1962306a36Sopenharmony_ci#include <linux/serial.h>
2062306a36Sopenharmony_ci#include <linux/usb.h>
2162306a36Sopenharmony_ci#include <linux/usb/serial.h>
2262306a36Sopenharmony_ci#include <linux/uaccess.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define DRIVER_DESC "Moschip 7840/7820 USB Serial Driver"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/*
2762306a36Sopenharmony_ci * 16C50 UART register defines
2862306a36Sopenharmony_ci */
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define LCR_BITS_5             0x00	/* 5 bits/char */
3162306a36Sopenharmony_ci#define LCR_BITS_6             0x01	/* 6 bits/char */
3262306a36Sopenharmony_ci#define LCR_BITS_7             0x02	/* 7 bits/char */
3362306a36Sopenharmony_ci#define LCR_BITS_8             0x03	/* 8 bits/char */
3462306a36Sopenharmony_ci#define LCR_BITS_MASK          0x03	/* Mask for bits/char field */
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#define LCR_STOP_1             0x00	/* 1 stop bit */
3762306a36Sopenharmony_ci#define LCR_STOP_1_5           0x04	/* 1.5 stop bits (if 5   bits/char) */
3862306a36Sopenharmony_ci#define LCR_STOP_2             0x04	/* 2 stop bits   (if 6-8 bits/char) */
3962306a36Sopenharmony_ci#define LCR_STOP_MASK          0x04	/* Mask for stop bits field */
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#define LCR_PAR_NONE           0x00	/* No parity */
4262306a36Sopenharmony_ci#define LCR_PAR_ODD            0x08	/* Odd parity */
4362306a36Sopenharmony_ci#define LCR_PAR_EVEN           0x18	/* Even parity */
4462306a36Sopenharmony_ci#define LCR_PAR_MARK           0x28	/* Force parity bit to 1 */
4562306a36Sopenharmony_ci#define LCR_PAR_SPACE          0x38	/* Force parity bit to 0 */
4662306a36Sopenharmony_ci#define LCR_PAR_MASK           0x38	/* Mask for parity field */
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#define LCR_SET_BREAK          0x40	/* Set Break condition */
4962306a36Sopenharmony_ci#define LCR_DL_ENABLE          0x80	/* Enable access to divisor latch */
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci#define MCR_DTR                0x01	/* Assert DTR */
5262306a36Sopenharmony_ci#define MCR_RTS                0x02	/* Assert RTS */
5362306a36Sopenharmony_ci#define MCR_OUT1               0x04	/* Loopback only: Sets state of RI */
5462306a36Sopenharmony_ci#define MCR_MASTER_IE          0x08	/* Enable interrupt outputs */
5562306a36Sopenharmony_ci#define MCR_LOOPBACK           0x10	/* Set internal (digital) loopback mode */
5662306a36Sopenharmony_ci#define MCR_XON_ANY            0x20	/* Enable any char to exit XOFF mode */
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci#define MOS7840_MSR_CTS        0x10	/* Current state of CTS */
5962306a36Sopenharmony_ci#define MOS7840_MSR_DSR        0x20	/* Current state of DSR */
6062306a36Sopenharmony_ci#define MOS7840_MSR_RI         0x40	/* Current state of RI */
6162306a36Sopenharmony_ci#define MOS7840_MSR_CD         0x80	/* Current state of CD */
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci/*
6462306a36Sopenharmony_ci * Defines used for sending commands to port
6562306a36Sopenharmony_ci */
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci#define MOS_WDR_TIMEOUT		5000	/* default urb timeout */
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci#define MOS_PORT1       0x0200
7062306a36Sopenharmony_ci#define MOS_PORT2       0x0300
7162306a36Sopenharmony_ci#define MOS_VENREG      0x0000
7262306a36Sopenharmony_ci#define MOS_MAX_PORT	0x02
7362306a36Sopenharmony_ci#define MOS_WRITE       0x0E
7462306a36Sopenharmony_ci#define MOS_READ        0x0D
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci/* Requests */
7762306a36Sopenharmony_ci#define MCS_RD_RTYPE    0xC0
7862306a36Sopenharmony_ci#define MCS_WR_RTYPE    0x40
7962306a36Sopenharmony_ci#define MCS_RDREQ       0x0D
8062306a36Sopenharmony_ci#define MCS_WRREQ       0x0E
8162306a36Sopenharmony_ci#define MCS_CTRL_TIMEOUT        500
8262306a36Sopenharmony_ci#define VENDOR_READ_LENGTH      (0x01)
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci#define MAX_NAME_LEN    64
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci#define ZLP_REG1  0x3A		/* Zero_Flag_Reg1    58 */
8762306a36Sopenharmony_ci#define ZLP_REG5  0x3E		/* Zero_Flag_Reg5    62 */
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci/* For higher baud Rates use TIOCEXBAUD */
9062306a36Sopenharmony_ci#define TIOCEXBAUD     0x5462
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci/*
9362306a36Sopenharmony_ci * Vendor id and device id defines
9462306a36Sopenharmony_ci *
9562306a36Sopenharmony_ci * NOTE: Do not add new defines, add entries directly to the id_table instead.
9662306a36Sopenharmony_ci */
9762306a36Sopenharmony_ci#define USB_VENDOR_ID_BANDB              0x0856
9862306a36Sopenharmony_ci#define BANDB_DEVICE_ID_USO9ML2_2        0xAC22
9962306a36Sopenharmony_ci#define BANDB_DEVICE_ID_USO9ML2_2P       0xBC00
10062306a36Sopenharmony_ci#define BANDB_DEVICE_ID_USO9ML2_4        0xAC24
10162306a36Sopenharmony_ci#define BANDB_DEVICE_ID_USO9ML2_4P       0xBC01
10262306a36Sopenharmony_ci#define BANDB_DEVICE_ID_US9ML2_2         0xAC29
10362306a36Sopenharmony_ci#define BANDB_DEVICE_ID_US9ML2_4         0xAC30
10462306a36Sopenharmony_ci#define BANDB_DEVICE_ID_USPTL4_2         0xAC31
10562306a36Sopenharmony_ci#define BANDB_DEVICE_ID_USPTL4_4         0xAC32
10662306a36Sopenharmony_ci#define BANDB_DEVICE_ID_USOPTL4_2        0xAC42
10762306a36Sopenharmony_ci#define BANDB_DEVICE_ID_USOPTL4_2P       0xBC02
10862306a36Sopenharmony_ci#define BANDB_DEVICE_ID_USOPTL4_4        0xAC44
10962306a36Sopenharmony_ci#define BANDB_DEVICE_ID_USOPTL4_4P       0xBC03
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci/* Interrupt Routine Defines    */
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci#define SERIAL_IIR_RLS      0x06
11462306a36Sopenharmony_ci#define SERIAL_IIR_MS       0x00
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci/*
11762306a36Sopenharmony_ci *  Emulation of the bit mask on the LINE STATUS REGISTER.
11862306a36Sopenharmony_ci */
11962306a36Sopenharmony_ci#define SERIAL_LSR_DR       0x0001
12062306a36Sopenharmony_ci#define SERIAL_LSR_OE       0x0002
12162306a36Sopenharmony_ci#define SERIAL_LSR_PE       0x0004
12262306a36Sopenharmony_ci#define SERIAL_LSR_FE       0x0008
12362306a36Sopenharmony_ci#define SERIAL_LSR_BI       0x0010
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci#define MOS_MSR_DELTA_CTS   0x10
12662306a36Sopenharmony_ci#define MOS_MSR_DELTA_DSR   0x20
12762306a36Sopenharmony_ci#define MOS_MSR_DELTA_RI    0x40
12862306a36Sopenharmony_ci#define MOS_MSR_DELTA_CD    0x80
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci/* Serial Port register Address */
13162306a36Sopenharmony_ci#define INTERRUPT_ENABLE_REGISTER  ((__u16)(0x01))
13262306a36Sopenharmony_ci#define FIFO_CONTROL_REGISTER      ((__u16)(0x02))
13362306a36Sopenharmony_ci#define LINE_CONTROL_REGISTER      ((__u16)(0x03))
13462306a36Sopenharmony_ci#define MODEM_CONTROL_REGISTER     ((__u16)(0x04))
13562306a36Sopenharmony_ci#define LINE_STATUS_REGISTER       ((__u16)(0x05))
13662306a36Sopenharmony_ci#define MODEM_STATUS_REGISTER      ((__u16)(0x06))
13762306a36Sopenharmony_ci#define SCRATCH_PAD_REGISTER       ((__u16)(0x07))
13862306a36Sopenharmony_ci#define DIVISOR_LATCH_LSB          ((__u16)(0x00))
13962306a36Sopenharmony_ci#define DIVISOR_LATCH_MSB          ((__u16)(0x01))
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci#define CLK_MULTI_REGISTER         ((__u16)(0x02))
14262306a36Sopenharmony_ci#define CLK_START_VALUE_REGISTER   ((__u16)(0x03))
14362306a36Sopenharmony_ci#define GPIO_REGISTER              ((__u16)(0x07))
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci#define SERIAL_LCR_DLAB            ((__u16)(0x0080))
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci/*
14862306a36Sopenharmony_ci * URB POOL related defines
14962306a36Sopenharmony_ci */
15062306a36Sopenharmony_ci#define NUM_URBS                        16	/* URB Count */
15162306a36Sopenharmony_ci#define URB_TRANSFER_BUFFER_SIZE        32	/* URB Size  */
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci/* LED on/off milliseconds*/
15462306a36Sopenharmony_ci#define LED_ON_MS	500
15562306a36Sopenharmony_ci#define LED_OFF_MS	500
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cienum mos7840_flag {
15862306a36Sopenharmony_ci	MOS7840_FLAG_LED_BUSY,
15962306a36Sopenharmony_ci};
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci#define MCS_PORT_MASK	GENMASK(2, 0)
16262306a36Sopenharmony_ci#define MCS_PORTS(nr)	((nr) & MCS_PORT_MASK)
16362306a36Sopenharmony_ci#define MCS_LED		BIT(3)
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci#define MCS_DEVICE(vid, pid, flags) \
16662306a36Sopenharmony_ci		USB_DEVICE((vid), (pid)), .driver_info = (flags)
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic const struct usb_device_id id_table[] = {
16962306a36Sopenharmony_ci	{ MCS_DEVICE(0x0557, 0x2011, MCS_PORTS(4)) },	/* ATEN UC2324 */
17062306a36Sopenharmony_ci	{ MCS_DEVICE(0x0557, 0x7820, MCS_PORTS(2)) },	/* ATEN UC2322 */
17162306a36Sopenharmony_ci	{ MCS_DEVICE(0x110a, 0x2210, MCS_PORTS(2)) },	/* Moxa UPort 2210 */
17262306a36Sopenharmony_ci	{ MCS_DEVICE(0x9710, 0x7810, MCS_PORTS(1) | MCS_LED) }, /* ASIX MCS7810 */
17362306a36Sopenharmony_ci	{ MCS_DEVICE(0x9710, 0x7820, MCS_PORTS(2)) },	/* MosChip MCS7820 */
17462306a36Sopenharmony_ci	{ MCS_DEVICE(0x9710, 0x7840, MCS_PORTS(4)) },	/* MosChip MCS7840 */
17562306a36Sopenharmony_ci	{ MCS_DEVICE(0x9710, 0x7843, MCS_PORTS(3)) },	/* ASIX MCS7840 3 port */
17662306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_2) },
17762306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_2P) },
17862306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_4) },
17962306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_4P) },
18062306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_US9ML2_2) },
18162306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_US9ML2_4) },
18262306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USPTL4_2) },
18362306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USPTL4_4) },
18462306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_2) },
18562306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_2P) },
18662306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_4) },
18762306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_4P) },
18862306a36Sopenharmony_ci	{}			/* terminating entry */
18962306a36Sopenharmony_ci};
19062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, id_table);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci/* This structure holds all of the local port information */
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistruct moschip_port {
19562306a36Sopenharmony_ci	int port_num;		/*Actual port number in the device(1,2,etc) */
19662306a36Sopenharmony_ci	struct urb *read_urb;	/* read URB for this port */
19762306a36Sopenharmony_ci	__u8 shadowLCR;		/* last LCR value received */
19862306a36Sopenharmony_ci	__u8 shadowMCR;		/* last MCR value received */
19962306a36Sopenharmony_ci	struct usb_serial_port *port;	/* loop back to the owner of this object */
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	/* Offsets */
20262306a36Sopenharmony_ci	__u8 SpRegOffset;
20362306a36Sopenharmony_ci	__u8 ControlRegOffset;
20462306a36Sopenharmony_ci	__u8 DcrRegOffset;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	spinlock_t pool_lock;
20762306a36Sopenharmony_ci	struct urb *write_urb_pool[NUM_URBS];
20862306a36Sopenharmony_ci	char busy[NUM_URBS];
20962306a36Sopenharmony_ci	bool read_urb_busy;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	/* For device(s) with LED indicator */
21262306a36Sopenharmony_ci	bool has_led;
21362306a36Sopenharmony_ci	struct timer_list led_timer1;	/* Timer for LED on */
21462306a36Sopenharmony_ci	struct timer_list led_timer2;	/* Timer for LED off */
21562306a36Sopenharmony_ci	struct urb *led_urb;
21662306a36Sopenharmony_ci	struct usb_ctrlrequest *led_dr;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	unsigned long flags;
21962306a36Sopenharmony_ci};
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci/*
22262306a36Sopenharmony_ci * mos7840_set_reg_sync
22362306a36Sopenharmony_ci * 	To set the Control register by calling usb_fill_control_urb function
22462306a36Sopenharmony_ci *	by passing usb_sndctrlpipe function as parameter.
22562306a36Sopenharmony_ci */
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic int mos7840_set_reg_sync(struct usb_serial_port *port, __u16 reg,
22862306a36Sopenharmony_ci				__u16 val)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	struct usb_device *dev = port->serial->dev;
23162306a36Sopenharmony_ci	val = val & 0x00ff;
23262306a36Sopenharmony_ci	dev_dbg(&port->dev, "mos7840_set_reg_sync offset is %x, value %x\n", reg, val);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), MCS_WRREQ,
23562306a36Sopenharmony_ci			       MCS_WR_RTYPE, val, reg, NULL, 0,
23662306a36Sopenharmony_ci			       MOS_WDR_TIMEOUT);
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci/*
24062306a36Sopenharmony_ci * mos7840_get_reg_sync
24162306a36Sopenharmony_ci * 	To set the Uart register by calling usb_fill_control_urb function by
24262306a36Sopenharmony_ci *	passing usb_rcvctrlpipe function as parameter.
24362306a36Sopenharmony_ci */
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_cistatic int mos7840_get_reg_sync(struct usb_serial_port *port, __u16 reg,
24662306a36Sopenharmony_ci				__u16 *val)
24762306a36Sopenharmony_ci{
24862306a36Sopenharmony_ci	struct usb_device *dev = port->serial->dev;
24962306a36Sopenharmony_ci	int ret = 0;
25062306a36Sopenharmony_ci	u8 *buf;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	buf = kmalloc(VENDOR_READ_LENGTH, GFP_KERNEL);
25362306a36Sopenharmony_ci	if (!buf)
25462306a36Sopenharmony_ci		return -ENOMEM;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ,
25762306a36Sopenharmony_ci			      MCS_RD_RTYPE, 0, reg, buf, VENDOR_READ_LENGTH,
25862306a36Sopenharmony_ci			      MOS_WDR_TIMEOUT);
25962306a36Sopenharmony_ci	if (ret < VENDOR_READ_LENGTH) {
26062306a36Sopenharmony_ci		if (ret >= 0)
26162306a36Sopenharmony_ci			ret = -EIO;
26262306a36Sopenharmony_ci		goto out;
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	*val = buf[0];
26662306a36Sopenharmony_ci	dev_dbg(&port->dev, "%s offset is %x, return val %x\n", __func__, reg, *val);
26762306a36Sopenharmony_ciout:
26862306a36Sopenharmony_ci	kfree(buf);
26962306a36Sopenharmony_ci	return ret;
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci/*
27362306a36Sopenharmony_ci * mos7840_set_uart_reg
27462306a36Sopenharmony_ci *	To set the Uart register by calling usb_fill_control_urb function by
27562306a36Sopenharmony_ci *	passing usb_sndctrlpipe function as parameter.
27662306a36Sopenharmony_ci */
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_cistatic int mos7840_set_uart_reg(struct usb_serial_port *port, __u16 reg,
27962306a36Sopenharmony_ci				__u16 val)
28062306a36Sopenharmony_ci{
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	struct usb_device *dev = port->serial->dev;
28362306a36Sopenharmony_ci	val = val & 0x00ff;
28462306a36Sopenharmony_ci	/* For the UART control registers, the application number need
28562306a36Sopenharmony_ci	   to be Or'ed */
28662306a36Sopenharmony_ci	if (port->serial->num_ports == 2 && port->port_number != 0)
28762306a36Sopenharmony_ci		val |= ((__u16)port->port_number + 2) << 8;
28862306a36Sopenharmony_ci	else
28962306a36Sopenharmony_ci		val |= ((__u16)port->port_number + 1) << 8;
29062306a36Sopenharmony_ci	dev_dbg(&port->dev, "%s application number is %x\n", __func__, val);
29162306a36Sopenharmony_ci	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), MCS_WRREQ,
29262306a36Sopenharmony_ci			       MCS_WR_RTYPE, val, reg, NULL, 0,
29362306a36Sopenharmony_ci			       MOS_WDR_TIMEOUT);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci/*
29862306a36Sopenharmony_ci * mos7840_get_uart_reg
29962306a36Sopenharmony_ci *	To set the Control register by calling usb_fill_control_urb function
30062306a36Sopenharmony_ci *	by passing usb_rcvctrlpipe function as parameter.
30162306a36Sopenharmony_ci */
30262306a36Sopenharmony_cistatic int mos7840_get_uart_reg(struct usb_serial_port *port, __u16 reg,
30362306a36Sopenharmony_ci				__u16 *val)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	struct usb_device *dev = port->serial->dev;
30662306a36Sopenharmony_ci	int ret = 0;
30762306a36Sopenharmony_ci	__u16 Wval;
30862306a36Sopenharmony_ci	u8 *buf;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	buf = kmalloc(VENDOR_READ_LENGTH, GFP_KERNEL);
31162306a36Sopenharmony_ci	if (!buf)
31262306a36Sopenharmony_ci		return -ENOMEM;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	/* Wval  is same as application number */
31562306a36Sopenharmony_ci	if (port->serial->num_ports == 2 && port->port_number != 0)
31662306a36Sopenharmony_ci		Wval = ((__u16)port->port_number + 2) << 8;
31762306a36Sopenharmony_ci	else
31862306a36Sopenharmony_ci		Wval = ((__u16)port->port_number + 1) << 8;
31962306a36Sopenharmony_ci	dev_dbg(&port->dev, "%s application number is %x\n", __func__, Wval);
32062306a36Sopenharmony_ci	ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ,
32162306a36Sopenharmony_ci			      MCS_RD_RTYPE, Wval, reg, buf, VENDOR_READ_LENGTH,
32262306a36Sopenharmony_ci			      MOS_WDR_TIMEOUT);
32362306a36Sopenharmony_ci	if (ret < VENDOR_READ_LENGTH) {
32462306a36Sopenharmony_ci		if (ret >= 0)
32562306a36Sopenharmony_ci			ret = -EIO;
32662306a36Sopenharmony_ci		goto out;
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci	*val = buf[0];
32962306a36Sopenharmony_ciout:
33062306a36Sopenharmony_ci	kfree(buf);
33162306a36Sopenharmony_ci	return ret;
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistatic void mos7840_dump_serial_port(struct usb_serial_port *port,
33562306a36Sopenharmony_ci				     struct moschip_port *mos7840_port)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	dev_dbg(&port->dev, "SpRegOffset is %2x\n", mos7840_port->SpRegOffset);
33962306a36Sopenharmony_ci	dev_dbg(&port->dev, "ControlRegOffset is %2x\n", mos7840_port->ControlRegOffset);
34062306a36Sopenharmony_ci	dev_dbg(&port->dev, "DCRRegOffset is %2x\n", mos7840_port->DcrRegOffset);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci/************************************************************************/
34562306a36Sopenharmony_ci/************************************************************************/
34662306a36Sopenharmony_ci/*            U S B  C A L L B A C K   F U N C T I O N S                */
34762306a36Sopenharmony_ci/*            U S B  C A L L B A C K   F U N C T I O N S                */
34862306a36Sopenharmony_ci/************************************************************************/
34962306a36Sopenharmony_ci/************************************************************************/
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_cistatic void mos7840_set_led_callback(struct urb *urb)
35262306a36Sopenharmony_ci{
35362306a36Sopenharmony_ci	switch (urb->status) {
35462306a36Sopenharmony_ci	case 0:
35562306a36Sopenharmony_ci		/* Success */
35662306a36Sopenharmony_ci		break;
35762306a36Sopenharmony_ci	case -ECONNRESET:
35862306a36Sopenharmony_ci	case -ENOENT:
35962306a36Sopenharmony_ci	case -ESHUTDOWN:
36062306a36Sopenharmony_ci		/* This urb is terminated, clean up */
36162306a36Sopenharmony_ci		dev_dbg(&urb->dev->dev, "%s - urb shutting down: %d\n",
36262306a36Sopenharmony_ci			__func__, urb->status);
36362306a36Sopenharmony_ci		break;
36462306a36Sopenharmony_ci	default:
36562306a36Sopenharmony_ci		dev_dbg(&urb->dev->dev, "%s - nonzero urb status: %d\n",
36662306a36Sopenharmony_ci			__func__, urb->status);
36762306a36Sopenharmony_ci	}
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cistatic void mos7840_set_led_async(struct moschip_port *mcs, __u16 wval,
37162306a36Sopenharmony_ci				__u16 reg)
37262306a36Sopenharmony_ci{
37362306a36Sopenharmony_ci	struct usb_device *dev = mcs->port->serial->dev;
37462306a36Sopenharmony_ci	struct usb_ctrlrequest *dr = mcs->led_dr;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	dr->bRequestType = MCS_WR_RTYPE;
37762306a36Sopenharmony_ci	dr->bRequest = MCS_WRREQ;
37862306a36Sopenharmony_ci	dr->wValue = cpu_to_le16(wval);
37962306a36Sopenharmony_ci	dr->wIndex = cpu_to_le16(reg);
38062306a36Sopenharmony_ci	dr->wLength = cpu_to_le16(0);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	usb_fill_control_urb(mcs->led_urb, dev, usb_sndctrlpipe(dev, 0),
38362306a36Sopenharmony_ci		(unsigned char *)dr, NULL, 0, mos7840_set_led_callback, NULL);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	usb_submit_urb(mcs->led_urb, GFP_ATOMIC);
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_cistatic void mos7840_set_led_sync(struct usb_serial_port *port, __u16 reg,
38962306a36Sopenharmony_ci				__u16 val)
39062306a36Sopenharmony_ci{
39162306a36Sopenharmony_ci	struct usb_device *dev = port->serial->dev;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	usb_control_msg(dev, usb_sndctrlpipe(dev, 0), MCS_WRREQ, MCS_WR_RTYPE,
39462306a36Sopenharmony_ci			val, reg, NULL, 0, MOS_WDR_TIMEOUT);
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_cistatic void mos7840_led_off(struct timer_list *t)
39862306a36Sopenharmony_ci{
39962306a36Sopenharmony_ci	struct moschip_port *mcs = from_timer(mcs, t, led_timer1);
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	/* Turn off LED */
40262306a36Sopenharmony_ci	mos7840_set_led_async(mcs, 0x0300, MODEM_CONTROL_REGISTER);
40362306a36Sopenharmony_ci	mod_timer(&mcs->led_timer2,
40462306a36Sopenharmony_ci				jiffies + msecs_to_jiffies(LED_OFF_MS));
40562306a36Sopenharmony_ci}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_cistatic void mos7840_led_flag_off(struct timer_list *t)
40862306a36Sopenharmony_ci{
40962306a36Sopenharmony_ci	struct moschip_port *mcs = from_timer(mcs, t, led_timer2);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	clear_bit_unlock(MOS7840_FLAG_LED_BUSY, &mcs->flags);
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_cistatic void mos7840_led_activity(struct usb_serial_port *port)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	struct moschip_port *mos7840_port = usb_get_serial_port_data(port);
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	if (test_and_set_bit_lock(MOS7840_FLAG_LED_BUSY, &mos7840_port->flags))
41962306a36Sopenharmony_ci		return;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	mos7840_set_led_async(mos7840_port, 0x0301, MODEM_CONTROL_REGISTER);
42262306a36Sopenharmony_ci	mod_timer(&mos7840_port->led_timer1,
42362306a36Sopenharmony_ci				jiffies + msecs_to_jiffies(LED_ON_MS));
42462306a36Sopenharmony_ci}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci/*****************************************************************************
42762306a36Sopenharmony_ci * mos7840_bulk_in_callback
42862306a36Sopenharmony_ci *	this is the callback function for when we have received data on the
42962306a36Sopenharmony_ci *	bulk in endpoint.
43062306a36Sopenharmony_ci *****************************************************************************/
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_cistatic void mos7840_bulk_in_callback(struct urb *urb)
43362306a36Sopenharmony_ci{
43462306a36Sopenharmony_ci	struct moschip_port *mos7840_port = urb->context;
43562306a36Sopenharmony_ci	struct usb_serial_port *port = mos7840_port->port;
43662306a36Sopenharmony_ci	int retval;
43762306a36Sopenharmony_ci	unsigned char *data;
43862306a36Sopenharmony_ci	int status = urb->status;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	if (status) {
44162306a36Sopenharmony_ci		dev_dbg(&urb->dev->dev, "nonzero read bulk status received: %d\n", status);
44262306a36Sopenharmony_ci		mos7840_port->read_urb_busy = false;
44362306a36Sopenharmony_ci		return;
44462306a36Sopenharmony_ci	}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	data = urb->transfer_buffer;
44762306a36Sopenharmony_ci	usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	if (urb->actual_length) {
45062306a36Sopenharmony_ci		struct tty_port *tport = &mos7840_port->port->port;
45162306a36Sopenharmony_ci		tty_insert_flip_string(tport, data, urb->actual_length);
45262306a36Sopenharmony_ci		tty_flip_buffer_push(tport);
45362306a36Sopenharmony_ci		port->icount.rx += urb->actual_length;
45462306a36Sopenharmony_ci		dev_dbg(&port->dev, "icount.rx is %d:\n", port->icount.rx);
45562306a36Sopenharmony_ci	}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	if (mos7840_port->has_led)
45862306a36Sopenharmony_ci		mos7840_led_activity(port);
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	mos7840_port->read_urb_busy = true;
46162306a36Sopenharmony_ci	retval = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	if (retval) {
46462306a36Sopenharmony_ci		dev_dbg(&port->dev, "usb_submit_urb(read bulk) failed, retval = %d\n", retval);
46562306a36Sopenharmony_ci		mos7840_port->read_urb_busy = false;
46662306a36Sopenharmony_ci	}
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci/*****************************************************************************
47062306a36Sopenharmony_ci * mos7840_bulk_out_data_callback
47162306a36Sopenharmony_ci *	this is the callback function for when we have finished sending
47262306a36Sopenharmony_ci *	serial data on the bulk out endpoint.
47362306a36Sopenharmony_ci *****************************************************************************/
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_cistatic void mos7840_bulk_out_data_callback(struct urb *urb)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	struct moschip_port *mos7840_port = urb->context;
47862306a36Sopenharmony_ci	struct usb_serial_port *port = mos7840_port->port;
47962306a36Sopenharmony_ci	int status = urb->status;
48062306a36Sopenharmony_ci	unsigned long flags;
48162306a36Sopenharmony_ci	int i;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	spin_lock_irqsave(&mos7840_port->pool_lock, flags);
48462306a36Sopenharmony_ci	for (i = 0; i < NUM_URBS; i++) {
48562306a36Sopenharmony_ci		if (urb == mos7840_port->write_urb_pool[i]) {
48662306a36Sopenharmony_ci			mos7840_port->busy[i] = 0;
48762306a36Sopenharmony_ci			break;
48862306a36Sopenharmony_ci		}
48962306a36Sopenharmony_ci	}
49062306a36Sopenharmony_ci	spin_unlock_irqrestore(&mos7840_port->pool_lock, flags);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	if (status) {
49362306a36Sopenharmony_ci		dev_dbg(&port->dev, "nonzero write bulk status received:%d\n", status);
49462306a36Sopenharmony_ci		return;
49562306a36Sopenharmony_ci	}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	tty_port_tty_wakeup(&port->port);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci}
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci/************************************************************************/
50262306a36Sopenharmony_ci/*       D R I V E R  T T Y  I N T E R F A C E  F U N C T I O N S       */
50362306a36Sopenharmony_ci/************************************************************************/
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci/*****************************************************************************
50662306a36Sopenharmony_ci * mos7840_open
50762306a36Sopenharmony_ci *	this function is called by the tty driver when a port is opened
50862306a36Sopenharmony_ci *	If successful, we return 0
50962306a36Sopenharmony_ci *	Otherwise we return a negative error number.
51062306a36Sopenharmony_ci *****************************************************************************/
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_cistatic int mos7840_open(struct tty_struct *tty, struct usb_serial_port *port)
51362306a36Sopenharmony_ci{
51462306a36Sopenharmony_ci	struct moschip_port *mos7840_port = usb_get_serial_port_data(port);
51562306a36Sopenharmony_ci	struct usb_serial *serial = port->serial;
51662306a36Sopenharmony_ci	int response;
51762306a36Sopenharmony_ci	int j;
51862306a36Sopenharmony_ci	struct urb *urb;
51962306a36Sopenharmony_ci	__u16 Data;
52062306a36Sopenharmony_ci	int status;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	usb_clear_halt(serial->dev, port->write_urb->pipe);
52362306a36Sopenharmony_ci	usb_clear_halt(serial->dev, port->read_urb->pipe);
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	/* Initialising the write urb pool */
52662306a36Sopenharmony_ci	for (j = 0; j < NUM_URBS; ++j) {
52762306a36Sopenharmony_ci		urb = usb_alloc_urb(0, GFP_KERNEL);
52862306a36Sopenharmony_ci		mos7840_port->write_urb_pool[j] = urb;
52962306a36Sopenharmony_ci		if (!urb)
53062306a36Sopenharmony_ci			continue;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci		urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE,
53362306a36Sopenharmony_ci								GFP_KERNEL);
53462306a36Sopenharmony_ci		if (!urb->transfer_buffer) {
53562306a36Sopenharmony_ci			usb_free_urb(urb);
53662306a36Sopenharmony_ci			mos7840_port->write_urb_pool[j] = NULL;
53762306a36Sopenharmony_ci			continue;
53862306a36Sopenharmony_ci		}
53962306a36Sopenharmony_ci	}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci/*****************************************************************************
54262306a36Sopenharmony_ci * Initialize MCS7840 -- Write Init values to corresponding Registers
54362306a36Sopenharmony_ci *
54462306a36Sopenharmony_ci * Register Index
54562306a36Sopenharmony_ci * 1 : IER
54662306a36Sopenharmony_ci * 2 : FCR
54762306a36Sopenharmony_ci * 3 : LCR
54862306a36Sopenharmony_ci * 4 : MCR
54962306a36Sopenharmony_ci *
55062306a36Sopenharmony_ci * 0x08 : SP1/2 Control Reg
55162306a36Sopenharmony_ci *****************************************************************************/
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	/* NEED to check the following Block */
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	Data = 0x0;
55662306a36Sopenharmony_ci	status = mos7840_get_reg_sync(port, mos7840_port->SpRegOffset, &Data);
55762306a36Sopenharmony_ci	if (status < 0) {
55862306a36Sopenharmony_ci		dev_dbg(&port->dev, "Reading Spreg failed\n");
55962306a36Sopenharmony_ci		goto err;
56062306a36Sopenharmony_ci	}
56162306a36Sopenharmony_ci	Data |= 0x80;
56262306a36Sopenharmony_ci	status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data);
56362306a36Sopenharmony_ci	if (status < 0) {
56462306a36Sopenharmony_ci		dev_dbg(&port->dev, "writing Spreg failed\n");
56562306a36Sopenharmony_ci		goto err;
56662306a36Sopenharmony_ci	}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	Data &= ~0x80;
56962306a36Sopenharmony_ci	status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data);
57062306a36Sopenharmony_ci	if (status < 0) {
57162306a36Sopenharmony_ci		dev_dbg(&port->dev, "writing Spreg failed\n");
57262306a36Sopenharmony_ci		goto err;
57362306a36Sopenharmony_ci	}
57462306a36Sopenharmony_ci	/* End of block to be checked */
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	Data = 0x0;
57762306a36Sopenharmony_ci	status = mos7840_get_reg_sync(port, mos7840_port->ControlRegOffset,
57862306a36Sopenharmony_ci									&Data);
57962306a36Sopenharmony_ci	if (status < 0) {
58062306a36Sopenharmony_ci		dev_dbg(&port->dev, "Reading Controlreg failed\n");
58162306a36Sopenharmony_ci		goto err;
58262306a36Sopenharmony_ci	}
58362306a36Sopenharmony_ci	Data |= 0x08;		/* Driver done bit */
58462306a36Sopenharmony_ci	Data |= 0x20;		/* rx_disable */
58562306a36Sopenharmony_ci	status = mos7840_set_reg_sync(port,
58662306a36Sopenharmony_ci				mos7840_port->ControlRegOffset, Data);
58762306a36Sopenharmony_ci	if (status < 0) {
58862306a36Sopenharmony_ci		dev_dbg(&port->dev, "writing Controlreg failed\n");
58962306a36Sopenharmony_ci		goto err;
59062306a36Sopenharmony_ci	}
59162306a36Sopenharmony_ci	/* do register settings here */
59262306a36Sopenharmony_ci	/* Set all regs to the device default values. */
59362306a36Sopenharmony_ci	/***********************************
59462306a36Sopenharmony_ci	 * First Disable all interrupts.
59562306a36Sopenharmony_ci	 ***********************************/
59662306a36Sopenharmony_ci	Data = 0x00;
59762306a36Sopenharmony_ci	status = mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
59862306a36Sopenharmony_ci	if (status < 0) {
59962306a36Sopenharmony_ci		dev_dbg(&port->dev, "disabling interrupts failed\n");
60062306a36Sopenharmony_ci		goto err;
60162306a36Sopenharmony_ci	}
60262306a36Sopenharmony_ci	/* Set FIFO_CONTROL_REGISTER to the default value */
60362306a36Sopenharmony_ci	Data = 0x00;
60462306a36Sopenharmony_ci	status = mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data);
60562306a36Sopenharmony_ci	if (status < 0) {
60662306a36Sopenharmony_ci		dev_dbg(&port->dev, "Writing FIFO_CONTROL_REGISTER  failed\n");
60762306a36Sopenharmony_ci		goto err;
60862306a36Sopenharmony_ci	}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	Data = 0xcf;
61162306a36Sopenharmony_ci	status = mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data);
61262306a36Sopenharmony_ci	if (status < 0) {
61362306a36Sopenharmony_ci		dev_dbg(&port->dev, "Writing FIFO_CONTROL_REGISTER  failed\n");
61462306a36Sopenharmony_ci		goto err;
61562306a36Sopenharmony_ci	}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	Data = 0x03;
61862306a36Sopenharmony_ci	status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
61962306a36Sopenharmony_ci	mos7840_port->shadowLCR = Data;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	Data = 0x0b;
62262306a36Sopenharmony_ci	status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
62362306a36Sopenharmony_ci	mos7840_port->shadowMCR = Data;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	Data = 0x00;
62662306a36Sopenharmony_ci	status = mos7840_get_uart_reg(port, LINE_CONTROL_REGISTER, &Data);
62762306a36Sopenharmony_ci	mos7840_port->shadowLCR = Data;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	Data |= SERIAL_LCR_DLAB;	/* data latch enable in LCR 0x80 */
63062306a36Sopenharmony_ci	status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	Data = 0x0c;
63362306a36Sopenharmony_ci	status = mos7840_set_uart_reg(port, DIVISOR_LATCH_LSB, Data);
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	Data = 0x0;
63662306a36Sopenharmony_ci	status = mos7840_set_uart_reg(port, DIVISOR_LATCH_MSB, Data);
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	Data = 0x00;
63962306a36Sopenharmony_ci	status = mos7840_get_uart_reg(port, LINE_CONTROL_REGISTER, &Data);
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	Data = Data & ~SERIAL_LCR_DLAB;
64262306a36Sopenharmony_ci	status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
64362306a36Sopenharmony_ci	mos7840_port->shadowLCR = Data;
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	/* clearing Bulkin and Bulkout Fifo */
64662306a36Sopenharmony_ci	Data = 0x0;
64762306a36Sopenharmony_ci	status = mos7840_get_reg_sync(port, mos7840_port->SpRegOffset, &Data);
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	Data = Data | 0x0c;
65062306a36Sopenharmony_ci	status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data);
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	Data = Data & ~0x0c;
65362306a36Sopenharmony_ci	status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data);
65462306a36Sopenharmony_ci	/* Finally enable all interrupts */
65562306a36Sopenharmony_ci	Data = 0x0c;
65662306a36Sopenharmony_ci	status = mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	/* clearing rx_disable */
65962306a36Sopenharmony_ci	Data = 0x0;
66062306a36Sopenharmony_ci	status = mos7840_get_reg_sync(port, mos7840_port->ControlRegOffset,
66162306a36Sopenharmony_ci									&Data);
66262306a36Sopenharmony_ci	Data = Data & ~0x20;
66362306a36Sopenharmony_ci	status = mos7840_set_reg_sync(port, mos7840_port->ControlRegOffset,
66462306a36Sopenharmony_ci									Data);
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	/* rx_negate */
66762306a36Sopenharmony_ci	Data = 0x0;
66862306a36Sopenharmony_ci	status = mos7840_get_reg_sync(port, mos7840_port->ControlRegOffset,
66962306a36Sopenharmony_ci									&Data);
67062306a36Sopenharmony_ci	Data = Data | 0x10;
67162306a36Sopenharmony_ci	status = mos7840_set_reg_sync(port, mos7840_port->ControlRegOffset,
67262306a36Sopenharmony_ci									Data);
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	dev_dbg(&port->dev, "port number is %d\n", port->port_number);
67562306a36Sopenharmony_ci	dev_dbg(&port->dev, "minor number is %d\n", port->minor);
67662306a36Sopenharmony_ci	dev_dbg(&port->dev, "Bulkin endpoint is %d\n", port->bulk_in_endpointAddress);
67762306a36Sopenharmony_ci	dev_dbg(&port->dev, "BulkOut endpoint is %d\n", port->bulk_out_endpointAddress);
67862306a36Sopenharmony_ci	dev_dbg(&port->dev, "Interrupt endpoint is %d\n", port->interrupt_in_endpointAddress);
67962306a36Sopenharmony_ci	dev_dbg(&port->dev, "port's number in the device is %d\n", mos7840_port->port_num);
68062306a36Sopenharmony_ci	mos7840_port->read_urb = port->read_urb;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	/* set up our bulk in urb */
68362306a36Sopenharmony_ci	if ((serial->num_ports == 2) && (((__u16)port->port_number % 2) != 0)) {
68462306a36Sopenharmony_ci		usb_fill_bulk_urb(mos7840_port->read_urb,
68562306a36Sopenharmony_ci			serial->dev,
68662306a36Sopenharmony_ci			usb_rcvbulkpipe(serial->dev,
68762306a36Sopenharmony_ci				(port->bulk_in_endpointAddress) + 2),
68862306a36Sopenharmony_ci			port->bulk_in_buffer,
68962306a36Sopenharmony_ci			mos7840_port->read_urb->transfer_buffer_length,
69062306a36Sopenharmony_ci			mos7840_bulk_in_callback, mos7840_port);
69162306a36Sopenharmony_ci	} else {
69262306a36Sopenharmony_ci		usb_fill_bulk_urb(mos7840_port->read_urb,
69362306a36Sopenharmony_ci			serial->dev,
69462306a36Sopenharmony_ci			usb_rcvbulkpipe(serial->dev,
69562306a36Sopenharmony_ci				port->bulk_in_endpointAddress),
69662306a36Sopenharmony_ci			port->bulk_in_buffer,
69762306a36Sopenharmony_ci			mos7840_port->read_urb->transfer_buffer_length,
69862306a36Sopenharmony_ci			mos7840_bulk_in_callback, mos7840_port);
69962306a36Sopenharmony_ci	}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	dev_dbg(&port->dev, "%s: bulkin endpoint is %d\n", __func__, port->bulk_in_endpointAddress);
70262306a36Sopenharmony_ci	mos7840_port->read_urb_busy = true;
70362306a36Sopenharmony_ci	response = usb_submit_urb(mos7840_port->read_urb, GFP_KERNEL);
70462306a36Sopenharmony_ci	if (response) {
70562306a36Sopenharmony_ci		dev_err(&port->dev, "%s - Error %d submitting control urb\n",
70662306a36Sopenharmony_ci			__func__, response);
70762306a36Sopenharmony_ci		mos7840_port->read_urb_busy = false;
70862306a36Sopenharmony_ci	}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	/* initialize our port settings */
71162306a36Sopenharmony_ci	/* Must set to enable ints! */
71262306a36Sopenharmony_ci	mos7840_port->shadowMCR = MCR_MASTER_IE;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	return 0;
71562306a36Sopenharmony_cierr:
71662306a36Sopenharmony_ci	for (j = 0; j < NUM_URBS; ++j) {
71762306a36Sopenharmony_ci		urb = mos7840_port->write_urb_pool[j];
71862306a36Sopenharmony_ci		if (!urb)
71962306a36Sopenharmony_ci			continue;
72062306a36Sopenharmony_ci		kfree(urb->transfer_buffer);
72162306a36Sopenharmony_ci		usb_free_urb(urb);
72262306a36Sopenharmony_ci	}
72362306a36Sopenharmony_ci	return status;
72462306a36Sopenharmony_ci}
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci/*****************************************************************************
72762306a36Sopenharmony_ci * mos7840_chars_in_buffer
72862306a36Sopenharmony_ci *	this function is called by the tty driver when it wants to know how many
72962306a36Sopenharmony_ci *	bytes of data we currently have outstanding in the port (data that has
73062306a36Sopenharmony_ci *	been written, but hasn't made it out the port yet)
73162306a36Sopenharmony_ci *****************************************************************************/
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_cistatic unsigned int mos7840_chars_in_buffer(struct tty_struct *tty)
73462306a36Sopenharmony_ci{
73562306a36Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
73662306a36Sopenharmony_ci	struct moschip_port *mos7840_port = usb_get_serial_port_data(port);
73762306a36Sopenharmony_ci	int i;
73862306a36Sopenharmony_ci	unsigned int chars = 0;
73962306a36Sopenharmony_ci	unsigned long flags;
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	spin_lock_irqsave(&mos7840_port->pool_lock, flags);
74262306a36Sopenharmony_ci	for (i = 0; i < NUM_URBS; ++i) {
74362306a36Sopenharmony_ci		if (mos7840_port->busy[i]) {
74462306a36Sopenharmony_ci			struct urb *urb = mos7840_port->write_urb_pool[i];
74562306a36Sopenharmony_ci			chars += urb->transfer_buffer_length;
74662306a36Sopenharmony_ci		}
74762306a36Sopenharmony_ci	}
74862306a36Sopenharmony_ci	spin_unlock_irqrestore(&mos7840_port->pool_lock, flags);
74962306a36Sopenharmony_ci	dev_dbg(&port->dev, "%s - returns %u\n", __func__, chars);
75062306a36Sopenharmony_ci	return chars;
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci}
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci/*****************************************************************************
75562306a36Sopenharmony_ci * mos7840_close
75662306a36Sopenharmony_ci *	this function is called by the tty driver when a port is closed
75762306a36Sopenharmony_ci *****************************************************************************/
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_cistatic void mos7840_close(struct usb_serial_port *port)
76062306a36Sopenharmony_ci{
76162306a36Sopenharmony_ci	struct moschip_port *mos7840_port = usb_get_serial_port_data(port);
76262306a36Sopenharmony_ci	int j;
76362306a36Sopenharmony_ci	__u16 Data;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	for (j = 0; j < NUM_URBS; ++j)
76662306a36Sopenharmony_ci		usb_kill_urb(mos7840_port->write_urb_pool[j]);
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	/* Freeing Write URBs */
76962306a36Sopenharmony_ci	for (j = 0; j < NUM_URBS; ++j) {
77062306a36Sopenharmony_ci		if (mos7840_port->write_urb_pool[j]) {
77162306a36Sopenharmony_ci			kfree(mos7840_port->write_urb_pool[j]->transfer_buffer);
77262306a36Sopenharmony_ci			usb_free_urb(mos7840_port->write_urb_pool[j]);
77362306a36Sopenharmony_ci		}
77462306a36Sopenharmony_ci	}
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	usb_kill_urb(mos7840_port->read_urb);
77762306a36Sopenharmony_ci	mos7840_port->read_urb_busy = false;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	Data = 0x0;
78062306a36Sopenharmony_ci	mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	Data = 0x00;
78362306a36Sopenharmony_ci	mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
78462306a36Sopenharmony_ci}
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci/*****************************************************************************
78762306a36Sopenharmony_ci * mos7840_break
78862306a36Sopenharmony_ci *	this function sends a break to the port
78962306a36Sopenharmony_ci *****************************************************************************/
79062306a36Sopenharmony_cistatic int mos7840_break(struct tty_struct *tty, int break_state)
79162306a36Sopenharmony_ci{
79262306a36Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
79362306a36Sopenharmony_ci	struct moschip_port *mos7840_port = usb_get_serial_port_data(port);
79462306a36Sopenharmony_ci	unsigned char data;
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	if (break_state == -1)
79762306a36Sopenharmony_ci		data = mos7840_port->shadowLCR | LCR_SET_BREAK;
79862306a36Sopenharmony_ci	else
79962306a36Sopenharmony_ci		data = mos7840_port->shadowLCR & ~LCR_SET_BREAK;
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	/* FIXME: no locking on shadowLCR anywhere in driver */
80262306a36Sopenharmony_ci	mos7840_port->shadowLCR = data;
80362306a36Sopenharmony_ci	dev_dbg(&port->dev, "%s mos7840_port->shadowLCR is %x\n", __func__, mos7840_port->shadowLCR);
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	return mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER,
80662306a36Sopenharmony_ci				    mos7840_port->shadowLCR);
80762306a36Sopenharmony_ci}
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci/*****************************************************************************
81062306a36Sopenharmony_ci * mos7840_write_room
81162306a36Sopenharmony_ci *	this function is called by the tty driver when it wants to know how many
81262306a36Sopenharmony_ci *	bytes of data we can accept for a specific port.
81362306a36Sopenharmony_ci *****************************************************************************/
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_cistatic unsigned int mos7840_write_room(struct tty_struct *tty)
81662306a36Sopenharmony_ci{
81762306a36Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
81862306a36Sopenharmony_ci	struct moschip_port *mos7840_port = usb_get_serial_port_data(port);
81962306a36Sopenharmony_ci	int i;
82062306a36Sopenharmony_ci	unsigned int room = 0;
82162306a36Sopenharmony_ci	unsigned long flags;
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	spin_lock_irqsave(&mos7840_port->pool_lock, flags);
82462306a36Sopenharmony_ci	for (i = 0; i < NUM_URBS; ++i) {
82562306a36Sopenharmony_ci		if (!mos7840_port->busy[i])
82662306a36Sopenharmony_ci			room += URB_TRANSFER_BUFFER_SIZE;
82762306a36Sopenharmony_ci	}
82862306a36Sopenharmony_ci	spin_unlock_irqrestore(&mos7840_port->pool_lock, flags);
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	room = (room == 0) ? 0 : room - URB_TRANSFER_BUFFER_SIZE + 1;
83162306a36Sopenharmony_ci	dev_dbg(&mos7840_port->port->dev, "%s - returns %u\n", __func__, room);
83262306a36Sopenharmony_ci	return room;
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci}
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci/*****************************************************************************
83762306a36Sopenharmony_ci * mos7840_write
83862306a36Sopenharmony_ci *	this function is called by the tty driver when data should be written to
83962306a36Sopenharmony_ci *	the port.
84062306a36Sopenharmony_ci *	If successful, we return the number of bytes written, otherwise we
84162306a36Sopenharmony_ci *      return a negative error number.
84262306a36Sopenharmony_ci *****************************************************************************/
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_cistatic int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port,
84562306a36Sopenharmony_ci			 const unsigned char *data, int count)
84662306a36Sopenharmony_ci{
84762306a36Sopenharmony_ci	struct moschip_port *mos7840_port = usb_get_serial_port_data(port);
84862306a36Sopenharmony_ci	struct usb_serial *serial = port->serial;
84962306a36Sopenharmony_ci	int status;
85062306a36Sopenharmony_ci	int i;
85162306a36Sopenharmony_ci	int bytes_sent = 0;
85262306a36Sopenharmony_ci	int transfer_size;
85362306a36Sopenharmony_ci	unsigned long flags;
85462306a36Sopenharmony_ci	struct urb *urb;
85562306a36Sopenharmony_ci	/* __u16 Data; */
85662306a36Sopenharmony_ci	const unsigned char *current_position = data;
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	/* try to find a free urb in the list */
85962306a36Sopenharmony_ci	urb = NULL;
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	spin_lock_irqsave(&mos7840_port->pool_lock, flags);
86262306a36Sopenharmony_ci	for (i = 0; i < NUM_URBS; ++i) {
86362306a36Sopenharmony_ci		if (!mos7840_port->busy[i]) {
86462306a36Sopenharmony_ci			mos7840_port->busy[i] = 1;
86562306a36Sopenharmony_ci			urb = mos7840_port->write_urb_pool[i];
86662306a36Sopenharmony_ci			dev_dbg(&port->dev, "URB:%d\n", i);
86762306a36Sopenharmony_ci			break;
86862306a36Sopenharmony_ci		}
86962306a36Sopenharmony_ci	}
87062306a36Sopenharmony_ci	spin_unlock_irqrestore(&mos7840_port->pool_lock, flags);
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	if (urb == NULL) {
87362306a36Sopenharmony_ci		dev_dbg(&port->dev, "%s - no more free urbs\n", __func__);
87462306a36Sopenharmony_ci		goto exit;
87562306a36Sopenharmony_ci	}
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	if (urb->transfer_buffer == NULL) {
87862306a36Sopenharmony_ci		urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE,
87962306a36Sopenharmony_ci					       GFP_ATOMIC);
88062306a36Sopenharmony_ci		if (!urb->transfer_buffer) {
88162306a36Sopenharmony_ci			bytes_sent = -ENOMEM;
88262306a36Sopenharmony_ci			goto exit;
88362306a36Sopenharmony_ci		}
88462306a36Sopenharmony_ci	}
88562306a36Sopenharmony_ci	transfer_size = min(count, URB_TRANSFER_BUFFER_SIZE);
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	memcpy(urb->transfer_buffer, current_position, transfer_size);
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	/* fill urb with data and submit  */
89062306a36Sopenharmony_ci	if ((serial->num_ports == 2) && (((__u16)port->port_number % 2) != 0)) {
89162306a36Sopenharmony_ci		usb_fill_bulk_urb(urb,
89262306a36Sopenharmony_ci			serial->dev,
89362306a36Sopenharmony_ci			usb_sndbulkpipe(serial->dev,
89462306a36Sopenharmony_ci				(port->bulk_out_endpointAddress) + 2),
89562306a36Sopenharmony_ci			urb->transfer_buffer,
89662306a36Sopenharmony_ci			transfer_size,
89762306a36Sopenharmony_ci			mos7840_bulk_out_data_callback, mos7840_port);
89862306a36Sopenharmony_ci	} else {
89962306a36Sopenharmony_ci		usb_fill_bulk_urb(urb,
90062306a36Sopenharmony_ci			serial->dev,
90162306a36Sopenharmony_ci			usb_sndbulkpipe(serial->dev,
90262306a36Sopenharmony_ci				port->bulk_out_endpointAddress),
90362306a36Sopenharmony_ci			urb->transfer_buffer,
90462306a36Sopenharmony_ci			transfer_size,
90562306a36Sopenharmony_ci			mos7840_bulk_out_data_callback, mos7840_port);
90662306a36Sopenharmony_ci	}
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	dev_dbg(&port->dev, "bulkout endpoint is %d\n", port->bulk_out_endpointAddress);
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	if (mos7840_port->has_led)
91162306a36Sopenharmony_ci		mos7840_led_activity(port);
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	/* send it down the pipe */
91462306a36Sopenharmony_ci	status = usb_submit_urb(urb, GFP_ATOMIC);
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	if (status) {
91762306a36Sopenharmony_ci		mos7840_port->busy[i] = 0;
91862306a36Sopenharmony_ci		dev_err_console(port, "%s - usb_submit_urb(write bulk) failed "
91962306a36Sopenharmony_ci			"with status = %d\n", __func__, status);
92062306a36Sopenharmony_ci		bytes_sent = status;
92162306a36Sopenharmony_ci		goto exit;
92262306a36Sopenharmony_ci	}
92362306a36Sopenharmony_ci	bytes_sent = transfer_size;
92462306a36Sopenharmony_ci	port->icount.tx += transfer_size;
92562306a36Sopenharmony_ci	dev_dbg(&port->dev, "icount.tx is %d:\n", port->icount.tx);
92662306a36Sopenharmony_ciexit:
92762306a36Sopenharmony_ci	return bytes_sent;
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci}
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci/*****************************************************************************
93262306a36Sopenharmony_ci * mos7840_throttle
93362306a36Sopenharmony_ci *	this function is called by the tty driver when it wants to stop the data
93462306a36Sopenharmony_ci *	being read from the port.
93562306a36Sopenharmony_ci *****************************************************************************/
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_cistatic void mos7840_throttle(struct tty_struct *tty)
93862306a36Sopenharmony_ci{
93962306a36Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
94062306a36Sopenharmony_ci	struct moschip_port *mos7840_port = usb_get_serial_port_data(port);
94162306a36Sopenharmony_ci	int status;
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	/* if we are implementing XON/XOFF, send the stop character */
94462306a36Sopenharmony_ci	if (I_IXOFF(tty)) {
94562306a36Sopenharmony_ci		unsigned char stop_char = STOP_CHAR(tty);
94662306a36Sopenharmony_ci		status = mos7840_write(tty, port, &stop_char, 1);
94762306a36Sopenharmony_ci		if (status <= 0)
94862306a36Sopenharmony_ci			return;
94962306a36Sopenharmony_ci	}
95062306a36Sopenharmony_ci	/* if we are implementing RTS/CTS, toggle that line */
95162306a36Sopenharmony_ci	if (C_CRTSCTS(tty)) {
95262306a36Sopenharmony_ci		mos7840_port->shadowMCR &= ~MCR_RTS;
95362306a36Sopenharmony_ci		status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER,
95462306a36Sopenharmony_ci					 mos7840_port->shadowMCR);
95562306a36Sopenharmony_ci		if (status < 0)
95662306a36Sopenharmony_ci			return;
95762306a36Sopenharmony_ci	}
95862306a36Sopenharmony_ci}
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci/*****************************************************************************
96162306a36Sopenharmony_ci * mos7840_unthrottle
96262306a36Sopenharmony_ci *	this function is called by the tty driver when it wants to resume
96362306a36Sopenharmony_ci *	the data being read from the port (called after mos7840_throttle is
96462306a36Sopenharmony_ci *	called)
96562306a36Sopenharmony_ci *****************************************************************************/
96662306a36Sopenharmony_cistatic void mos7840_unthrottle(struct tty_struct *tty)
96762306a36Sopenharmony_ci{
96862306a36Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
96962306a36Sopenharmony_ci	struct moschip_port *mos7840_port = usb_get_serial_port_data(port);
97062306a36Sopenharmony_ci	int status;
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	/* if we are implementing XON/XOFF, send the start character */
97362306a36Sopenharmony_ci	if (I_IXOFF(tty)) {
97462306a36Sopenharmony_ci		unsigned char start_char = START_CHAR(tty);
97562306a36Sopenharmony_ci		status = mos7840_write(tty, port, &start_char, 1);
97662306a36Sopenharmony_ci		if (status <= 0)
97762306a36Sopenharmony_ci			return;
97862306a36Sopenharmony_ci	}
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	/* if we are implementing RTS/CTS, toggle that line */
98162306a36Sopenharmony_ci	if (C_CRTSCTS(tty)) {
98262306a36Sopenharmony_ci		mos7840_port->shadowMCR |= MCR_RTS;
98362306a36Sopenharmony_ci		status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER,
98462306a36Sopenharmony_ci					 mos7840_port->shadowMCR);
98562306a36Sopenharmony_ci		if (status < 0)
98662306a36Sopenharmony_ci			return;
98762306a36Sopenharmony_ci	}
98862306a36Sopenharmony_ci}
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_cistatic int mos7840_tiocmget(struct tty_struct *tty)
99162306a36Sopenharmony_ci{
99262306a36Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
99362306a36Sopenharmony_ci	unsigned int result;
99462306a36Sopenharmony_ci	__u16 msr;
99562306a36Sopenharmony_ci	__u16 mcr;
99662306a36Sopenharmony_ci	int status;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	status = mos7840_get_uart_reg(port, MODEM_STATUS_REGISTER, &msr);
99962306a36Sopenharmony_ci	if (status < 0)
100062306a36Sopenharmony_ci		return -EIO;
100162306a36Sopenharmony_ci	status = mos7840_get_uart_reg(port, MODEM_CONTROL_REGISTER, &mcr);
100262306a36Sopenharmony_ci	if (status < 0)
100362306a36Sopenharmony_ci		return -EIO;
100462306a36Sopenharmony_ci	result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0)
100562306a36Sopenharmony_ci	    | ((mcr & MCR_RTS) ? TIOCM_RTS : 0)
100662306a36Sopenharmony_ci	    | ((mcr & MCR_LOOPBACK) ? TIOCM_LOOP : 0)
100762306a36Sopenharmony_ci	    | ((msr & MOS7840_MSR_CTS) ? TIOCM_CTS : 0)
100862306a36Sopenharmony_ci	    | ((msr & MOS7840_MSR_CD) ? TIOCM_CAR : 0)
100962306a36Sopenharmony_ci	    | ((msr & MOS7840_MSR_RI) ? TIOCM_RI : 0)
101062306a36Sopenharmony_ci	    | ((msr & MOS7840_MSR_DSR) ? TIOCM_DSR : 0);
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	dev_dbg(&port->dev, "%s - 0x%04X\n", __func__, result);
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	return result;
101562306a36Sopenharmony_ci}
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_cistatic int mos7840_tiocmset(struct tty_struct *tty,
101862306a36Sopenharmony_ci			    unsigned int set, unsigned int clear)
101962306a36Sopenharmony_ci{
102062306a36Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
102162306a36Sopenharmony_ci	struct moschip_port *mos7840_port = usb_get_serial_port_data(port);
102262306a36Sopenharmony_ci	unsigned int mcr;
102362306a36Sopenharmony_ci	int status;
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	/* FIXME: What locks the port registers ? */
102662306a36Sopenharmony_ci	mcr = mos7840_port->shadowMCR;
102762306a36Sopenharmony_ci	if (clear & TIOCM_RTS)
102862306a36Sopenharmony_ci		mcr &= ~MCR_RTS;
102962306a36Sopenharmony_ci	if (clear & TIOCM_DTR)
103062306a36Sopenharmony_ci		mcr &= ~MCR_DTR;
103162306a36Sopenharmony_ci	if (clear & TIOCM_LOOP)
103262306a36Sopenharmony_ci		mcr &= ~MCR_LOOPBACK;
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci	if (set & TIOCM_RTS)
103562306a36Sopenharmony_ci		mcr |= MCR_RTS;
103662306a36Sopenharmony_ci	if (set & TIOCM_DTR)
103762306a36Sopenharmony_ci		mcr |= MCR_DTR;
103862306a36Sopenharmony_ci	if (set & TIOCM_LOOP)
103962306a36Sopenharmony_ci		mcr |= MCR_LOOPBACK;
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	mos7840_port->shadowMCR = mcr;
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, mcr);
104462306a36Sopenharmony_ci	if (status < 0) {
104562306a36Sopenharmony_ci		dev_dbg(&port->dev, "setting MODEM_CONTROL_REGISTER Failed\n");
104662306a36Sopenharmony_ci		return status;
104762306a36Sopenharmony_ci	}
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	return 0;
105062306a36Sopenharmony_ci}
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci/*****************************************************************************
105362306a36Sopenharmony_ci * mos7840_calc_baud_rate_divisor
105462306a36Sopenharmony_ci *	this function calculates the proper baud rate divisor for the specified
105562306a36Sopenharmony_ci *	baud rate.
105662306a36Sopenharmony_ci *****************************************************************************/
105762306a36Sopenharmony_cistatic int mos7840_calc_baud_rate_divisor(struct usb_serial_port *port,
105862306a36Sopenharmony_ci					  int baudRate, int *divisor,
105962306a36Sopenharmony_ci					  __u16 *clk_sel_val)
106062306a36Sopenharmony_ci{
106162306a36Sopenharmony_ci	dev_dbg(&port->dev, "%s - %d\n", __func__, baudRate);
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	if (baudRate <= 115200) {
106462306a36Sopenharmony_ci		*divisor = 115200 / baudRate;
106562306a36Sopenharmony_ci		*clk_sel_val = 0x0;
106662306a36Sopenharmony_ci	}
106762306a36Sopenharmony_ci	if ((baudRate > 115200) && (baudRate <= 230400)) {
106862306a36Sopenharmony_ci		*divisor = 230400 / baudRate;
106962306a36Sopenharmony_ci		*clk_sel_val = 0x10;
107062306a36Sopenharmony_ci	} else if ((baudRate > 230400) && (baudRate <= 403200)) {
107162306a36Sopenharmony_ci		*divisor = 403200 / baudRate;
107262306a36Sopenharmony_ci		*clk_sel_val = 0x20;
107362306a36Sopenharmony_ci	} else if ((baudRate > 403200) && (baudRate <= 460800)) {
107462306a36Sopenharmony_ci		*divisor = 460800 / baudRate;
107562306a36Sopenharmony_ci		*clk_sel_val = 0x30;
107662306a36Sopenharmony_ci	} else if ((baudRate > 460800) && (baudRate <= 806400)) {
107762306a36Sopenharmony_ci		*divisor = 806400 / baudRate;
107862306a36Sopenharmony_ci		*clk_sel_val = 0x40;
107962306a36Sopenharmony_ci	} else if ((baudRate > 806400) && (baudRate <= 921600)) {
108062306a36Sopenharmony_ci		*divisor = 921600 / baudRate;
108162306a36Sopenharmony_ci		*clk_sel_val = 0x50;
108262306a36Sopenharmony_ci	} else if ((baudRate > 921600) && (baudRate <= 1572864)) {
108362306a36Sopenharmony_ci		*divisor = 1572864 / baudRate;
108462306a36Sopenharmony_ci		*clk_sel_val = 0x60;
108562306a36Sopenharmony_ci	} else if ((baudRate > 1572864) && (baudRate <= 3145728)) {
108662306a36Sopenharmony_ci		*divisor = 3145728 / baudRate;
108762306a36Sopenharmony_ci		*clk_sel_val = 0x70;
108862306a36Sopenharmony_ci	}
108962306a36Sopenharmony_ci	return 0;
109062306a36Sopenharmony_ci}
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci/*****************************************************************************
109362306a36Sopenharmony_ci * mos7840_send_cmd_write_baud_rate
109462306a36Sopenharmony_ci *	this function sends the proper command to change the baud rate of the
109562306a36Sopenharmony_ci *	specified port.
109662306a36Sopenharmony_ci *****************************************************************************/
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_cistatic int mos7840_send_cmd_write_baud_rate(struct moschip_port *mos7840_port,
109962306a36Sopenharmony_ci					    int baudRate)
110062306a36Sopenharmony_ci{
110162306a36Sopenharmony_ci	struct usb_serial_port *port = mos7840_port->port;
110262306a36Sopenharmony_ci	int divisor = 0;
110362306a36Sopenharmony_ci	int status;
110462306a36Sopenharmony_ci	__u16 Data;
110562306a36Sopenharmony_ci	__u16 clk_sel_val;
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	dev_dbg(&port->dev, "%s - baud = %d\n", __func__, baudRate);
110862306a36Sopenharmony_ci	/* reset clk_uart_sel in spregOffset */
110962306a36Sopenharmony_ci	if (baudRate > 115200) {
111062306a36Sopenharmony_ci#ifdef HW_flow_control
111162306a36Sopenharmony_ci		/* NOTE: need to see the pther register to modify */
111262306a36Sopenharmony_ci		/* setting h/w flow control bit to 1 */
111362306a36Sopenharmony_ci		Data = 0x2b;
111462306a36Sopenharmony_ci		mos7840_port->shadowMCR = Data;
111562306a36Sopenharmony_ci		status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER,
111662306a36Sopenharmony_ci									Data);
111762306a36Sopenharmony_ci		if (status < 0) {
111862306a36Sopenharmony_ci			dev_dbg(&port->dev, "Writing spreg failed in set_serial_baud\n");
111962306a36Sopenharmony_ci			return -1;
112062306a36Sopenharmony_ci		}
112162306a36Sopenharmony_ci#endif
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	} else {
112462306a36Sopenharmony_ci#ifdef HW_flow_control
112562306a36Sopenharmony_ci		/* setting h/w flow control bit to 0 */
112662306a36Sopenharmony_ci		Data = 0xb;
112762306a36Sopenharmony_ci		mos7840_port->shadowMCR = Data;
112862306a36Sopenharmony_ci		status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER,
112962306a36Sopenharmony_ci									Data);
113062306a36Sopenharmony_ci		if (status < 0) {
113162306a36Sopenharmony_ci			dev_dbg(&port->dev, "Writing spreg failed in set_serial_baud\n");
113262306a36Sopenharmony_ci			return -1;
113362306a36Sopenharmony_ci		}
113462306a36Sopenharmony_ci#endif
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	}
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	if (1) {		/* baudRate <= 115200) */
113962306a36Sopenharmony_ci		clk_sel_val = 0x0;
114062306a36Sopenharmony_ci		Data = 0x0;
114162306a36Sopenharmony_ci		status = mos7840_calc_baud_rate_divisor(port, baudRate, &divisor,
114262306a36Sopenharmony_ci						   &clk_sel_val);
114362306a36Sopenharmony_ci		status = mos7840_get_reg_sync(port, mos7840_port->SpRegOffset,
114462306a36Sopenharmony_ci								 &Data);
114562306a36Sopenharmony_ci		if (status < 0) {
114662306a36Sopenharmony_ci			dev_dbg(&port->dev, "reading spreg failed in set_serial_baud\n");
114762306a36Sopenharmony_ci			return -1;
114862306a36Sopenharmony_ci		}
114962306a36Sopenharmony_ci		Data = (Data & 0x8f) | clk_sel_val;
115062306a36Sopenharmony_ci		status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset,
115162306a36Sopenharmony_ci								Data);
115262306a36Sopenharmony_ci		if (status < 0) {
115362306a36Sopenharmony_ci			dev_dbg(&port->dev, "Writing spreg failed in set_serial_baud\n");
115462306a36Sopenharmony_ci			return -1;
115562306a36Sopenharmony_ci		}
115662306a36Sopenharmony_ci		/* Calculate the Divisor */
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci		if (status) {
115962306a36Sopenharmony_ci			dev_err(&port->dev, "%s - bad baud rate\n", __func__);
116062306a36Sopenharmony_ci			return status;
116162306a36Sopenharmony_ci		}
116262306a36Sopenharmony_ci		/* Enable access to divisor latch */
116362306a36Sopenharmony_ci		Data = mos7840_port->shadowLCR | SERIAL_LCR_DLAB;
116462306a36Sopenharmony_ci		mos7840_port->shadowLCR = Data;
116562306a36Sopenharmony_ci		mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci		/* Write the divisor */
116862306a36Sopenharmony_ci		Data = (unsigned char)(divisor & 0xff);
116962306a36Sopenharmony_ci		dev_dbg(&port->dev, "set_serial_baud Value to write DLL is %x\n", Data);
117062306a36Sopenharmony_ci		mos7840_set_uart_reg(port, DIVISOR_LATCH_LSB, Data);
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci		Data = (unsigned char)((divisor & 0xff00) >> 8);
117362306a36Sopenharmony_ci		dev_dbg(&port->dev, "set_serial_baud Value to write DLM is %x\n", Data);
117462306a36Sopenharmony_ci		mos7840_set_uart_reg(port, DIVISOR_LATCH_MSB, Data);
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci		/* Disable access to divisor latch */
117762306a36Sopenharmony_ci		Data = mos7840_port->shadowLCR & ~SERIAL_LCR_DLAB;
117862306a36Sopenharmony_ci		mos7840_port->shadowLCR = Data;
117962306a36Sopenharmony_ci		mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci	}
118262306a36Sopenharmony_ci	return status;
118362306a36Sopenharmony_ci}
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci/*****************************************************************************
118662306a36Sopenharmony_ci * mos7840_change_port_settings
118762306a36Sopenharmony_ci *	This routine is called to set the UART on the device to match
118862306a36Sopenharmony_ci *      the specified new settings.
118962306a36Sopenharmony_ci *****************************************************************************/
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_cistatic void mos7840_change_port_settings(struct tty_struct *tty,
119262306a36Sopenharmony_ci					 struct moschip_port *mos7840_port,
119362306a36Sopenharmony_ci					 const struct ktermios *old_termios)
119462306a36Sopenharmony_ci{
119562306a36Sopenharmony_ci	struct usb_serial_port *port = mos7840_port->port;
119662306a36Sopenharmony_ci	int baud;
119762306a36Sopenharmony_ci	unsigned cflag;
119862306a36Sopenharmony_ci	__u8 lData;
119962306a36Sopenharmony_ci	__u8 lParity;
120062306a36Sopenharmony_ci	__u8 lStop;
120162306a36Sopenharmony_ci	int status;
120262306a36Sopenharmony_ci	__u16 Data;
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci	lData = LCR_BITS_8;
120562306a36Sopenharmony_ci	lStop = LCR_STOP_1;
120662306a36Sopenharmony_ci	lParity = LCR_PAR_NONE;
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	cflag = tty->termios.c_cflag;
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	/* Change the number of bits */
121162306a36Sopenharmony_ci	switch (cflag & CSIZE) {
121262306a36Sopenharmony_ci	case CS5:
121362306a36Sopenharmony_ci		lData = LCR_BITS_5;
121462306a36Sopenharmony_ci		break;
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci	case CS6:
121762306a36Sopenharmony_ci		lData = LCR_BITS_6;
121862306a36Sopenharmony_ci		break;
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	case CS7:
122162306a36Sopenharmony_ci		lData = LCR_BITS_7;
122262306a36Sopenharmony_ci		break;
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	default:
122562306a36Sopenharmony_ci	case CS8:
122662306a36Sopenharmony_ci		lData = LCR_BITS_8;
122762306a36Sopenharmony_ci		break;
122862306a36Sopenharmony_ci	}
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	/* Change the Parity bit */
123162306a36Sopenharmony_ci	if (cflag & PARENB) {
123262306a36Sopenharmony_ci		if (cflag & PARODD) {
123362306a36Sopenharmony_ci			lParity = LCR_PAR_ODD;
123462306a36Sopenharmony_ci			dev_dbg(&port->dev, "%s - parity = odd\n", __func__);
123562306a36Sopenharmony_ci		} else {
123662306a36Sopenharmony_ci			lParity = LCR_PAR_EVEN;
123762306a36Sopenharmony_ci			dev_dbg(&port->dev, "%s - parity = even\n", __func__);
123862306a36Sopenharmony_ci		}
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci	} else {
124162306a36Sopenharmony_ci		dev_dbg(&port->dev, "%s - parity = none\n", __func__);
124262306a36Sopenharmony_ci	}
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci	if (cflag & CMSPAR)
124562306a36Sopenharmony_ci		lParity = lParity | 0x20;
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	/* Change the Stop bit */
124862306a36Sopenharmony_ci	if (cflag & CSTOPB) {
124962306a36Sopenharmony_ci		lStop = LCR_STOP_2;
125062306a36Sopenharmony_ci		dev_dbg(&port->dev, "%s - stop bits = 2\n", __func__);
125162306a36Sopenharmony_ci	} else {
125262306a36Sopenharmony_ci		lStop = LCR_STOP_1;
125362306a36Sopenharmony_ci		dev_dbg(&port->dev, "%s - stop bits = 1\n", __func__);
125462306a36Sopenharmony_ci	}
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	/* Update the LCR with the correct value */
125762306a36Sopenharmony_ci	mos7840_port->shadowLCR &=
125862306a36Sopenharmony_ci	    ~(LCR_BITS_MASK | LCR_STOP_MASK | LCR_PAR_MASK);
125962306a36Sopenharmony_ci	mos7840_port->shadowLCR |= (lData | lParity | lStop);
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci	dev_dbg(&port->dev, "%s - mos7840_port->shadowLCR is %x\n", __func__,
126262306a36Sopenharmony_ci		mos7840_port->shadowLCR);
126362306a36Sopenharmony_ci	/* Disable Interrupts */
126462306a36Sopenharmony_ci	Data = 0x00;
126562306a36Sopenharmony_ci	mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	Data = 0x00;
126862306a36Sopenharmony_ci	mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data);
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	Data = 0xcf;
127162306a36Sopenharmony_ci	mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data);
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci	/* Send the updated LCR value to the mos7840 */
127462306a36Sopenharmony_ci	Data = mos7840_port->shadowLCR;
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci	mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	Data = 0x00b;
127962306a36Sopenharmony_ci	mos7840_port->shadowMCR = Data;
128062306a36Sopenharmony_ci	mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
128162306a36Sopenharmony_ci	Data = 0x00b;
128262306a36Sopenharmony_ci	mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	/* set up the MCR register and send it to the mos7840 */
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	mos7840_port->shadowMCR = MCR_MASTER_IE;
128762306a36Sopenharmony_ci	if (cflag & CBAUD)
128862306a36Sopenharmony_ci		mos7840_port->shadowMCR |= (MCR_DTR | MCR_RTS);
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci	if (cflag & CRTSCTS)
129162306a36Sopenharmony_ci		mos7840_port->shadowMCR |= (MCR_XON_ANY);
129262306a36Sopenharmony_ci	else
129362306a36Sopenharmony_ci		mos7840_port->shadowMCR &= ~(MCR_XON_ANY);
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	Data = mos7840_port->shadowMCR;
129662306a36Sopenharmony_ci	mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	/* Determine divisor based on baud rate */
129962306a36Sopenharmony_ci	baud = tty_get_baud_rate(tty);
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	if (!baud) {
130262306a36Sopenharmony_ci		/* pick a default, any default... */
130362306a36Sopenharmony_ci		dev_dbg(&port->dev, "%s", "Picked default baud...\n");
130462306a36Sopenharmony_ci		baud = 9600;
130562306a36Sopenharmony_ci	}
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	dev_dbg(&port->dev, "%s - baud rate = %d\n", __func__, baud);
130862306a36Sopenharmony_ci	status = mos7840_send_cmd_write_baud_rate(mos7840_port, baud);
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci	/* Enable Interrupts */
131162306a36Sopenharmony_ci	Data = 0x0c;
131262306a36Sopenharmony_ci	mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	if (!mos7840_port->read_urb_busy) {
131562306a36Sopenharmony_ci		mos7840_port->read_urb_busy = true;
131662306a36Sopenharmony_ci		status = usb_submit_urb(mos7840_port->read_urb, GFP_KERNEL);
131762306a36Sopenharmony_ci		if (status) {
131862306a36Sopenharmony_ci			dev_dbg(&port->dev, "usb_submit_urb(read bulk) failed, status = %d\n",
131962306a36Sopenharmony_ci			    status);
132062306a36Sopenharmony_ci			mos7840_port->read_urb_busy = false;
132162306a36Sopenharmony_ci		}
132262306a36Sopenharmony_ci	}
132362306a36Sopenharmony_ci	dev_dbg(&port->dev, "%s - mos7840_port->shadowLCR is End %x\n", __func__,
132462306a36Sopenharmony_ci		mos7840_port->shadowLCR);
132562306a36Sopenharmony_ci}
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci/*****************************************************************************
132862306a36Sopenharmony_ci * mos7840_set_termios
132962306a36Sopenharmony_ci *	this function is called by the tty driver when it wants to change
133062306a36Sopenharmony_ci *	the termios structure
133162306a36Sopenharmony_ci *****************************************************************************/
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_cistatic void mos7840_set_termios(struct tty_struct *tty,
133462306a36Sopenharmony_ci				struct usb_serial_port *port,
133562306a36Sopenharmony_ci				const struct ktermios *old_termios)
133662306a36Sopenharmony_ci{
133762306a36Sopenharmony_ci	struct moschip_port *mos7840_port = usb_get_serial_port_data(port);
133862306a36Sopenharmony_ci	int status;
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci	/* change the port settings to the new ones specified */
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci	mos7840_change_port_settings(tty, mos7840_port, old_termios);
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	if (!mos7840_port->read_urb_busy) {
134562306a36Sopenharmony_ci		mos7840_port->read_urb_busy = true;
134662306a36Sopenharmony_ci		status = usb_submit_urb(mos7840_port->read_urb, GFP_KERNEL);
134762306a36Sopenharmony_ci		if (status) {
134862306a36Sopenharmony_ci			dev_dbg(&port->dev, "usb_submit_urb(read bulk) failed, status = %d\n",
134962306a36Sopenharmony_ci			    status);
135062306a36Sopenharmony_ci			mos7840_port->read_urb_busy = false;
135162306a36Sopenharmony_ci		}
135262306a36Sopenharmony_ci	}
135362306a36Sopenharmony_ci}
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci/*****************************************************************************
135662306a36Sopenharmony_ci * mos7840_get_lsr_info - get line status register info
135762306a36Sopenharmony_ci *
135862306a36Sopenharmony_ci * Purpose: Let user call ioctl() to get info when the UART physically
135962306a36Sopenharmony_ci * 	    is emptied.  On bus types like RS485, the transmitter must
136062306a36Sopenharmony_ci * 	    release the bus after transmitting. This must be done when
136162306a36Sopenharmony_ci * 	    the transmit shift register is empty, not be done when the
136262306a36Sopenharmony_ci * 	    transmit holding register is empty.  This functionality
136362306a36Sopenharmony_ci * 	    allows an RS485 driver to be written in user space.
136462306a36Sopenharmony_ci *****************************************************************************/
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_cistatic int mos7840_get_lsr_info(struct tty_struct *tty,
136762306a36Sopenharmony_ci				unsigned int __user *value)
136862306a36Sopenharmony_ci{
136962306a36Sopenharmony_ci	int count;
137062306a36Sopenharmony_ci	unsigned int result = 0;
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci	count = mos7840_chars_in_buffer(tty);
137362306a36Sopenharmony_ci	if (count == 0)
137462306a36Sopenharmony_ci		result = TIOCSER_TEMT;
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci	if (copy_to_user(value, &result, sizeof(int)))
137762306a36Sopenharmony_ci		return -EFAULT;
137862306a36Sopenharmony_ci	return 0;
137962306a36Sopenharmony_ci}
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci/*****************************************************************************
138262306a36Sopenharmony_ci * SerialIoctl
138362306a36Sopenharmony_ci *	this function handles any ioctl calls to the driver
138462306a36Sopenharmony_ci *****************************************************************************/
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_cistatic int mos7840_ioctl(struct tty_struct *tty,
138762306a36Sopenharmony_ci			 unsigned int cmd, unsigned long arg)
138862306a36Sopenharmony_ci{
138962306a36Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
139062306a36Sopenharmony_ci	void __user *argp = (void __user *)arg;
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci	switch (cmd) {
139362306a36Sopenharmony_ci		/* return number of bytes available */
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	case TIOCSERGETLSR:
139662306a36Sopenharmony_ci		dev_dbg(&port->dev, "%s TIOCSERGETLSR\n", __func__);
139762306a36Sopenharmony_ci		return mos7840_get_lsr_info(tty, argp);
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci	default:
140062306a36Sopenharmony_ci		break;
140162306a36Sopenharmony_ci	}
140262306a36Sopenharmony_ci	return -ENOIOCTLCMD;
140362306a36Sopenharmony_ci}
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_ci/*
140662306a36Sopenharmony_ci * Check if GPO (pin 42) is connected to GPI (pin 33) as recommended by ASIX
140762306a36Sopenharmony_ci * for MCS7810 by bit-banging a 16-bit word.
140862306a36Sopenharmony_ci *
140962306a36Sopenharmony_ci * Note that GPO is really RTS of the third port so this will toggle RTS of
141062306a36Sopenharmony_ci * port two or three on two- and four-port devices.
141162306a36Sopenharmony_ci */
141262306a36Sopenharmony_cistatic int mos7810_check(struct usb_serial *serial)
141362306a36Sopenharmony_ci{
141462306a36Sopenharmony_ci	int i, pass_count = 0;
141562306a36Sopenharmony_ci	u8 *buf;
141662306a36Sopenharmony_ci	__u16 data = 0, mcr_data = 0;
141762306a36Sopenharmony_ci	__u16 test_pattern = 0x55AA;
141862306a36Sopenharmony_ci	int res;
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	buf = kmalloc(VENDOR_READ_LENGTH, GFP_KERNEL);
142162306a36Sopenharmony_ci	if (!buf)
142262306a36Sopenharmony_ci		return 0;	/* failed to identify 7810 */
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci	/* Store MCR setting */
142562306a36Sopenharmony_ci	res = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
142662306a36Sopenharmony_ci		MCS_RDREQ, MCS_RD_RTYPE, 0x0300, MODEM_CONTROL_REGISTER,
142762306a36Sopenharmony_ci		buf, VENDOR_READ_LENGTH, MOS_WDR_TIMEOUT);
142862306a36Sopenharmony_ci	if (res == VENDOR_READ_LENGTH)
142962306a36Sopenharmony_ci		mcr_data = *buf;
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci	for (i = 0; i < 16; i++) {
143262306a36Sopenharmony_ci		/* Send the 1-bit test pattern out to MCS7810 test pin */
143362306a36Sopenharmony_ci		usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
143462306a36Sopenharmony_ci			MCS_WRREQ, MCS_WR_RTYPE,
143562306a36Sopenharmony_ci			(0x0300 | (((test_pattern >> i) & 0x0001) << 1)),
143662306a36Sopenharmony_ci			MODEM_CONTROL_REGISTER, NULL, 0, MOS_WDR_TIMEOUT);
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci		/* Read the test pattern back */
143962306a36Sopenharmony_ci		res = usb_control_msg(serial->dev,
144062306a36Sopenharmony_ci				usb_rcvctrlpipe(serial->dev, 0), MCS_RDREQ,
144162306a36Sopenharmony_ci				MCS_RD_RTYPE, 0, GPIO_REGISTER, buf,
144262306a36Sopenharmony_ci				VENDOR_READ_LENGTH, MOS_WDR_TIMEOUT);
144362306a36Sopenharmony_ci		if (res == VENDOR_READ_LENGTH)
144462306a36Sopenharmony_ci			data = *buf;
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ci		/* If this is a MCS7810 device, both test patterns must match */
144762306a36Sopenharmony_ci		if (((test_pattern >> i) ^ (~data >> 1)) & 0x0001)
144862306a36Sopenharmony_ci			break;
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci		pass_count++;
145162306a36Sopenharmony_ci	}
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci	/* Restore MCR setting */
145462306a36Sopenharmony_ci	usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), MCS_WRREQ,
145562306a36Sopenharmony_ci		MCS_WR_RTYPE, 0x0300 | mcr_data, MODEM_CONTROL_REGISTER, NULL,
145662306a36Sopenharmony_ci		0, MOS_WDR_TIMEOUT);
145762306a36Sopenharmony_ci
145862306a36Sopenharmony_ci	kfree(buf);
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	if (pass_count == 16)
146162306a36Sopenharmony_ci		return 1;
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci	return 0;
146462306a36Sopenharmony_ci}
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_cistatic int mos7840_probe(struct usb_serial *serial,
146762306a36Sopenharmony_ci				const struct usb_device_id *id)
146862306a36Sopenharmony_ci{
146962306a36Sopenharmony_ci	unsigned long device_flags = id->driver_info;
147062306a36Sopenharmony_ci	u8 *buf;
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci	/* Skip device-type detection if we already have device flags. */
147362306a36Sopenharmony_ci	if (device_flags)
147462306a36Sopenharmony_ci		goto out;
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_ci	buf = kzalloc(VENDOR_READ_LENGTH, GFP_KERNEL);
147762306a36Sopenharmony_ci	if (!buf)
147862306a36Sopenharmony_ci		return -ENOMEM;
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci	usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
148162306a36Sopenharmony_ci			MCS_RDREQ, MCS_RD_RTYPE, 0, GPIO_REGISTER, buf,
148262306a36Sopenharmony_ci			VENDOR_READ_LENGTH, MOS_WDR_TIMEOUT);
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci	/* For a MCS7840 device GPIO0 must be set to 1 */
148562306a36Sopenharmony_ci	if (buf[0] & 0x01)
148662306a36Sopenharmony_ci		device_flags = MCS_PORTS(4);
148762306a36Sopenharmony_ci	else if (mos7810_check(serial))
148862306a36Sopenharmony_ci		device_flags = MCS_PORTS(1) | MCS_LED;
148962306a36Sopenharmony_ci	else
149062306a36Sopenharmony_ci		device_flags = MCS_PORTS(2);
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci	kfree(buf);
149362306a36Sopenharmony_ciout:
149462306a36Sopenharmony_ci	usb_set_serial_data(serial, (void *)device_flags);
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	return 0;
149762306a36Sopenharmony_ci}
149862306a36Sopenharmony_ci
149962306a36Sopenharmony_cistatic int mos7840_calc_num_ports(struct usb_serial *serial,
150062306a36Sopenharmony_ci					struct usb_serial_endpoints *epds)
150162306a36Sopenharmony_ci{
150262306a36Sopenharmony_ci	unsigned long device_flags = (unsigned long)usb_get_serial_data(serial);
150362306a36Sopenharmony_ci	int num_ports = MCS_PORTS(device_flags);
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci	if (num_ports == 0 || num_ports > 4)
150662306a36Sopenharmony_ci		return -ENODEV;
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci	if (epds->num_bulk_in < num_ports || epds->num_bulk_out < num_ports) {
150962306a36Sopenharmony_ci		dev_err(&serial->interface->dev, "missing endpoints\n");
151062306a36Sopenharmony_ci		return -ENODEV;
151162306a36Sopenharmony_ci	}
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_ci	return num_ports;
151462306a36Sopenharmony_ci}
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_cistatic int mos7840_attach(struct usb_serial *serial)
151762306a36Sopenharmony_ci{
151862306a36Sopenharmony_ci	struct device *dev = &serial->interface->dev;
151962306a36Sopenharmony_ci	int status;
152062306a36Sopenharmony_ci	u16 val;
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ci	/* Zero Length flag enable */
152362306a36Sopenharmony_ci	val = 0x0f;
152462306a36Sopenharmony_ci	status = mos7840_set_reg_sync(serial->port[0], ZLP_REG5, val);
152562306a36Sopenharmony_ci	if (status < 0)
152662306a36Sopenharmony_ci		dev_dbg(dev, "Writing ZLP_REG5 failed status-0x%x\n", status);
152762306a36Sopenharmony_ci	else
152862306a36Sopenharmony_ci		dev_dbg(dev, "ZLP_REG5 Writing success status%d\n", status);
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci	return status;
153162306a36Sopenharmony_ci}
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_cistatic int mos7840_port_probe(struct usb_serial_port *port)
153462306a36Sopenharmony_ci{
153562306a36Sopenharmony_ci	struct usb_serial *serial = port->serial;
153662306a36Sopenharmony_ci	unsigned long device_flags = (unsigned long)usb_get_serial_data(serial);
153762306a36Sopenharmony_ci	struct moschip_port *mos7840_port;
153862306a36Sopenharmony_ci	int status;
153962306a36Sopenharmony_ci	int pnum;
154062306a36Sopenharmony_ci	__u16 Data;
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ci	/* we set up the pointers to the endpoints in the mos7840_open *
154362306a36Sopenharmony_ci	 * function, as the structures aren't created yet.             */
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_ci	pnum = port->port_number;
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_ci	dev_dbg(&port->dev, "mos7840_startup: configuring port %d\n", pnum);
154862306a36Sopenharmony_ci	mos7840_port = kzalloc(sizeof(struct moschip_port), GFP_KERNEL);
154962306a36Sopenharmony_ci	if (!mos7840_port)
155062306a36Sopenharmony_ci		return -ENOMEM;
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci	/* Initialize all port interrupt end point to port 0 int
155362306a36Sopenharmony_ci	 * endpoint. Our device has only one interrupt end point
155462306a36Sopenharmony_ci	 * common to all port */
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_ci	mos7840_port->port = port;
155762306a36Sopenharmony_ci	spin_lock_init(&mos7840_port->pool_lock);
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci	/* minor is not initialised until later by
156062306a36Sopenharmony_ci	 * usb-serial.c:get_free_serial() and cannot therefore be used
156162306a36Sopenharmony_ci	 * to index device instances */
156262306a36Sopenharmony_ci	mos7840_port->port_num = pnum + 1;
156362306a36Sopenharmony_ci	dev_dbg(&port->dev, "port->minor = %d\n", port->minor);
156462306a36Sopenharmony_ci	dev_dbg(&port->dev, "mos7840_port->port_num = %d\n", mos7840_port->port_num);
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci	if (mos7840_port->port_num == 1) {
156762306a36Sopenharmony_ci		mos7840_port->SpRegOffset = 0x0;
156862306a36Sopenharmony_ci		mos7840_port->ControlRegOffset = 0x1;
156962306a36Sopenharmony_ci		mos7840_port->DcrRegOffset = 0x4;
157062306a36Sopenharmony_ci	} else {
157162306a36Sopenharmony_ci		u8 phy_num = mos7840_port->port_num;
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci		/* Port 2 in the 2-port case uses registers of port 3 */
157462306a36Sopenharmony_ci		if (serial->num_ports == 2)
157562306a36Sopenharmony_ci			phy_num = 3;
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_ci		mos7840_port->SpRegOffset = 0x8 + 2 * (phy_num - 2);
157862306a36Sopenharmony_ci		mos7840_port->ControlRegOffset = 0x9 + 2 * (phy_num - 2);
157962306a36Sopenharmony_ci		mos7840_port->DcrRegOffset = 0x16 + 3 * (phy_num - 2);
158062306a36Sopenharmony_ci	}
158162306a36Sopenharmony_ci	mos7840_dump_serial_port(port, mos7840_port);
158262306a36Sopenharmony_ci	usb_set_serial_port_data(port, mos7840_port);
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_ci	/* enable rx_disable bit in control register */
158562306a36Sopenharmony_ci	status = mos7840_get_reg_sync(port,
158662306a36Sopenharmony_ci			mos7840_port->ControlRegOffset, &Data);
158762306a36Sopenharmony_ci	if (status < 0) {
158862306a36Sopenharmony_ci		dev_dbg(&port->dev, "Reading ControlReg failed status-0x%x\n", status);
158962306a36Sopenharmony_ci		goto error;
159062306a36Sopenharmony_ci	} else
159162306a36Sopenharmony_ci		dev_dbg(&port->dev, "ControlReg Reading success val is %x, status%d\n", Data, status);
159262306a36Sopenharmony_ci	Data |= 0x08;	/* setting driver done bit */
159362306a36Sopenharmony_ci	Data |= 0x04;	/* sp1_bit to have cts change reflect in
159462306a36Sopenharmony_ci			   modem status reg */
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci	/* Data |= 0x20; //rx_disable bit */
159762306a36Sopenharmony_ci	status = mos7840_set_reg_sync(port,
159862306a36Sopenharmony_ci			mos7840_port->ControlRegOffset, Data);
159962306a36Sopenharmony_ci	if (status < 0) {
160062306a36Sopenharmony_ci		dev_dbg(&port->dev, "Writing ControlReg failed(rx_disable) status-0x%x\n", status);
160162306a36Sopenharmony_ci		goto error;
160262306a36Sopenharmony_ci	} else
160362306a36Sopenharmony_ci		dev_dbg(&port->dev, "ControlReg Writing success(rx_disable) status%d\n", status);
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_ci	/* Write default values in DCR (i.e 0x01 in DCR0, 0x05 in DCR2
160662306a36Sopenharmony_ci	   and 0x24 in DCR3 */
160762306a36Sopenharmony_ci	Data = 0x01;
160862306a36Sopenharmony_ci	status = mos7840_set_reg_sync(port,
160962306a36Sopenharmony_ci			(__u16) (mos7840_port->DcrRegOffset + 0), Data);
161062306a36Sopenharmony_ci	if (status < 0) {
161162306a36Sopenharmony_ci		dev_dbg(&port->dev, "Writing DCR0 failed status-0x%x\n", status);
161262306a36Sopenharmony_ci		goto error;
161362306a36Sopenharmony_ci	} else
161462306a36Sopenharmony_ci		dev_dbg(&port->dev, "DCR0 Writing success status%d\n", status);
161562306a36Sopenharmony_ci
161662306a36Sopenharmony_ci	Data = 0x05;
161762306a36Sopenharmony_ci	status = mos7840_set_reg_sync(port,
161862306a36Sopenharmony_ci			(__u16) (mos7840_port->DcrRegOffset + 1), Data);
161962306a36Sopenharmony_ci	if (status < 0) {
162062306a36Sopenharmony_ci		dev_dbg(&port->dev, "Writing DCR1 failed status-0x%x\n", status);
162162306a36Sopenharmony_ci		goto error;
162262306a36Sopenharmony_ci	} else
162362306a36Sopenharmony_ci		dev_dbg(&port->dev, "DCR1 Writing success status%d\n", status);
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	Data = 0x24;
162662306a36Sopenharmony_ci	status = mos7840_set_reg_sync(port,
162762306a36Sopenharmony_ci			(__u16) (mos7840_port->DcrRegOffset + 2), Data);
162862306a36Sopenharmony_ci	if (status < 0) {
162962306a36Sopenharmony_ci		dev_dbg(&port->dev, "Writing DCR2 failed status-0x%x\n", status);
163062306a36Sopenharmony_ci		goto error;
163162306a36Sopenharmony_ci	} else
163262306a36Sopenharmony_ci		dev_dbg(&port->dev, "DCR2 Writing success status%d\n", status);
163362306a36Sopenharmony_ci
163462306a36Sopenharmony_ci	/* write values in clkstart0x0 and clkmulti 0x20 */
163562306a36Sopenharmony_ci	Data = 0x0;
163662306a36Sopenharmony_ci	status = mos7840_set_reg_sync(port, CLK_START_VALUE_REGISTER, Data);
163762306a36Sopenharmony_ci	if (status < 0) {
163862306a36Sopenharmony_ci		dev_dbg(&port->dev, "Writing CLK_START_VALUE_REGISTER failed status-0x%x\n", status);
163962306a36Sopenharmony_ci		goto error;
164062306a36Sopenharmony_ci	} else
164162306a36Sopenharmony_ci		dev_dbg(&port->dev, "CLK_START_VALUE_REGISTER Writing success status%d\n", status);
164262306a36Sopenharmony_ci
164362306a36Sopenharmony_ci	Data = 0x20;
164462306a36Sopenharmony_ci	status = mos7840_set_reg_sync(port, CLK_MULTI_REGISTER, Data);
164562306a36Sopenharmony_ci	if (status < 0) {
164662306a36Sopenharmony_ci		dev_dbg(&port->dev, "Writing CLK_MULTI_REGISTER failed status-0x%x\n", status);
164762306a36Sopenharmony_ci		goto error;
164862306a36Sopenharmony_ci	} else
164962306a36Sopenharmony_ci		dev_dbg(&port->dev, "CLK_MULTI_REGISTER Writing success status%d\n", status);
165062306a36Sopenharmony_ci
165162306a36Sopenharmony_ci	/* write value 0x0 to scratchpad register */
165262306a36Sopenharmony_ci	Data = 0x00;
165362306a36Sopenharmony_ci	status = mos7840_set_uart_reg(port, SCRATCH_PAD_REGISTER, Data);
165462306a36Sopenharmony_ci	if (status < 0) {
165562306a36Sopenharmony_ci		dev_dbg(&port->dev, "Writing SCRATCH_PAD_REGISTER failed status-0x%x\n", status);
165662306a36Sopenharmony_ci		goto error;
165762306a36Sopenharmony_ci	} else
165862306a36Sopenharmony_ci		dev_dbg(&port->dev, "SCRATCH_PAD_REGISTER Writing success status%d\n", status);
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_ci	/* Zero Length flag register */
166162306a36Sopenharmony_ci	if ((mos7840_port->port_num != 1) && (serial->num_ports == 2)) {
166262306a36Sopenharmony_ci		Data = 0xff;
166362306a36Sopenharmony_ci		status = mos7840_set_reg_sync(port,
166462306a36Sopenharmony_ci				(__u16) (ZLP_REG1 +
166562306a36Sopenharmony_ci					((__u16)mos7840_port->port_num)), Data);
166662306a36Sopenharmony_ci		dev_dbg(&port->dev, "ZLIP offset %x\n",
166762306a36Sopenharmony_ci				(__u16)(ZLP_REG1 + ((__u16) mos7840_port->port_num)));
166862306a36Sopenharmony_ci		if (status < 0) {
166962306a36Sopenharmony_ci			dev_dbg(&port->dev, "Writing ZLP_REG%d failed status-0x%x\n", pnum + 2, status);
167062306a36Sopenharmony_ci			goto error;
167162306a36Sopenharmony_ci		} else
167262306a36Sopenharmony_ci			dev_dbg(&port->dev, "ZLP_REG%d Writing success status%d\n", pnum + 2, status);
167362306a36Sopenharmony_ci	} else {
167462306a36Sopenharmony_ci		Data = 0xff;
167562306a36Sopenharmony_ci		status = mos7840_set_reg_sync(port,
167662306a36Sopenharmony_ci				(__u16) (ZLP_REG1 +
167762306a36Sopenharmony_ci					((__u16)mos7840_port->port_num) - 0x1), Data);
167862306a36Sopenharmony_ci		dev_dbg(&port->dev, "ZLIP offset %x\n",
167962306a36Sopenharmony_ci				(__u16)(ZLP_REG1 + ((__u16) mos7840_port->port_num) - 0x1));
168062306a36Sopenharmony_ci		if (status < 0) {
168162306a36Sopenharmony_ci			dev_dbg(&port->dev, "Writing ZLP_REG%d failed status-0x%x\n", pnum + 1, status);
168262306a36Sopenharmony_ci			goto error;
168362306a36Sopenharmony_ci		} else
168462306a36Sopenharmony_ci			dev_dbg(&port->dev, "ZLP_REG%d Writing success status%d\n", pnum + 1, status);
168562306a36Sopenharmony_ci
168662306a36Sopenharmony_ci	}
168762306a36Sopenharmony_ci
168862306a36Sopenharmony_ci	mos7840_port->has_led = device_flags & MCS_LED;
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_ci	/* Initialize LED timers */
169162306a36Sopenharmony_ci	if (mos7840_port->has_led) {
169262306a36Sopenharmony_ci		mos7840_port->led_urb = usb_alloc_urb(0, GFP_KERNEL);
169362306a36Sopenharmony_ci		mos7840_port->led_dr = kmalloc(sizeof(*mos7840_port->led_dr),
169462306a36Sopenharmony_ci								GFP_KERNEL);
169562306a36Sopenharmony_ci		if (!mos7840_port->led_urb || !mos7840_port->led_dr) {
169662306a36Sopenharmony_ci			status = -ENOMEM;
169762306a36Sopenharmony_ci			goto error;
169862306a36Sopenharmony_ci		}
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_ci		timer_setup(&mos7840_port->led_timer1, mos7840_led_off, 0);
170162306a36Sopenharmony_ci		mos7840_port->led_timer1.expires =
170262306a36Sopenharmony_ci			jiffies + msecs_to_jiffies(LED_ON_MS);
170362306a36Sopenharmony_ci		timer_setup(&mos7840_port->led_timer2, mos7840_led_flag_off,
170462306a36Sopenharmony_ci			    0);
170562306a36Sopenharmony_ci		mos7840_port->led_timer2.expires =
170662306a36Sopenharmony_ci			jiffies + msecs_to_jiffies(LED_OFF_MS);
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_ci		/* Turn off LED */
170962306a36Sopenharmony_ci		mos7840_set_led_sync(port, MODEM_CONTROL_REGISTER, 0x0300);
171062306a36Sopenharmony_ci	}
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci	return 0;
171362306a36Sopenharmony_cierror:
171462306a36Sopenharmony_ci	kfree(mos7840_port->led_dr);
171562306a36Sopenharmony_ci	usb_free_urb(mos7840_port->led_urb);
171662306a36Sopenharmony_ci	kfree(mos7840_port);
171762306a36Sopenharmony_ci
171862306a36Sopenharmony_ci	return status;
171962306a36Sopenharmony_ci}
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_cistatic void mos7840_port_remove(struct usb_serial_port *port)
172262306a36Sopenharmony_ci{
172362306a36Sopenharmony_ci	struct moschip_port *mos7840_port = usb_get_serial_port_data(port);
172462306a36Sopenharmony_ci
172562306a36Sopenharmony_ci	if (mos7840_port->has_led) {
172662306a36Sopenharmony_ci		/* Turn off LED */
172762306a36Sopenharmony_ci		mos7840_set_led_sync(port, MODEM_CONTROL_REGISTER, 0x0300);
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_ci		timer_shutdown_sync(&mos7840_port->led_timer1);
173062306a36Sopenharmony_ci		timer_shutdown_sync(&mos7840_port->led_timer2);
173162306a36Sopenharmony_ci
173262306a36Sopenharmony_ci		usb_kill_urb(mos7840_port->led_urb);
173362306a36Sopenharmony_ci		usb_free_urb(mos7840_port->led_urb);
173462306a36Sopenharmony_ci		kfree(mos7840_port->led_dr);
173562306a36Sopenharmony_ci	}
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_ci	kfree(mos7840_port);
173862306a36Sopenharmony_ci}
173962306a36Sopenharmony_ci
174062306a36Sopenharmony_cistatic struct usb_serial_driver moschip7840_4port_device = {
174162306a36Sopenharmony_ci	.driver = {
174262306a36Sopenharmony_ci		   .owner = THIS_MODULE,
174362306a36Sopenharmony_ci		   .name = "mos7840",
174462306a36Sopenharmony_ci		   },
174562306a36Sopenharmony_ci	.description = DRIVER_DESC,
174662306a36Sopenharmony_ci	.id_table = id_table,
174762306a36Sopenharmony_ci	.num_interrupt_in = 1,
174862306a36Sopenharmony_ci	.open = mos7840_open,
174962306a36Sopenharmony_ci	.close = mos7840_close,
175062306a36Sopenharmony_ci	.write = mos7840_write,
175162306a36Sopenharmony_ci	.write_room = mos7840_write_room,
175262306a36Sopenharmony_ci	.chars_in_buffer = mos7840_chars_in_buffer,
175362306a36Sopenharmony_ci	.throttle = mos7840_throttle,
175462306a36Sopenharmony_ci	.unthrottle = mos7840_unthrottle,
175562306a36Sopenharmony_ci	.calc_num_ports = mos7840_calc_num_ports,
175662306a36Sopenharmony_ci	.probe = mos7840_probe,
175762306a36Sopenharmony_ci	.attach = mos7840_attach,
175862306a36Sopenharmony_ci	.ioctl = mos7840_ioctl,
175962306a36Sopenharmony_ci	.set_termios = mos7840_set_termios,
176062306a36Sopenharmony_ci	.break_ctl = mos7840_break,
176162306a36Sopenharmony_ci	.tiocmget = mos7840_tiocmget,
176262306a36Sopenharmony_ci	.tiocmset = mos7840_tiocmset,
176362306a36Sopenharmony_ci	.get_icount = usb_serial_generic_get_icount,
176462306a36Sopenharmony_ci	.port_probe = mos7840_port_probe,
176562306a36Sopenharmony_ci	.port_remove = mos7840_port_remove,
176662306a36Sopenharmony_ci	.read_bulk_callback = mos7840_bulk_in_callback,
176762306a36Sopenharmony_ci};
176862306a36Sopenharmony_ci
176962306a36Sopenharmony_cistatic struct usb_serial_driver * const serial_drivers[] = {
177062306a36Sopenharmony_ci	&moschip7840_4port_device, NULL
177162306a36Sopenharmony_ci};
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_cimodule_usb_serial_driver(serial_drivers, id_table);
177462306a36Sopenharmony_ci
177562306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
177662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1777