xref: /openbmc/linux/drivers/usb/serial/mos7840.c (revision 375cb533c00a237264cc1d810d22f70de30362c2)
15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
23f542974SPaul B Schroeder /*
33f542974SPaul B Schroeder  * Clean ups from Moschip version and a few ioctl implementations by:
43f542974SPaul B Schroeder  *	Paul B Schroeder <pschroeder "at" uplogix "dot" com>
53f542974SPaul B Schroeder  *
63f542974SPaul B Schroeder  * Originally based on drivers/usb/serial/io_edgeport.c which is:
73f542974SPaul B Schroeder  *      Copyright (C) 2000 Inside Out Networks, All rights reserved.
83f542974SPaul B Schroeder  *      Copyright (C) 2001-2002 Greg Kroah-Hartman <greg@kroah.com>
93f542974SPaul B Schroeder  *
103f542974SPaul B Schroeder  */
113f542974SPaul B Schroeder 
123f542974SPaul B Schroeder #include <linux/kernel.h>
133f542974SPaul B Schroeder #include <linux/errno.h>
143f542974SPaul B Schroeder #include <linux/slab.h>
153f542974SPaul B Schroeder #include <linux/tty.h>
163f542974SPaul B Schroeder #include <linux/tty_driver.h>
173f542974SPaul B Schroeder #include <linux/tty_flip.h>
183f542974SPaul B Schroeder #include <linux/module.h>
193f542974SPaul B Schroeder #include <linux/serial.h>
203f542974SPaul B Schroeder #include <linux/usb.h>
213f542974SPaul B Schroeder #include <linux/usb/serial.h>
22880af9dbSAlan Cox #include <linux/uaccess.h>
233f542974SPaul B Schroeder 
243f542974SPaul B Schroeder #define DRIVER_DESC "Moschip 7840/7820 USB Serial Driver"
253f542974SPaul B Schroeder 
263f542974SPaul B Schroeder /*
273f542974SPaul B Schroeder  * 16C50 UART register defines
283f542974SPaul B Schroeder  */
293f542974SPaul B Schroeder 
303f542974SPaul B Schroeder #define LCR_BITS_5             0x00	/* 5 bits/char */
313f542974SPaul B Schroeder #define LCR_BITS_6             0x01	/* 6 bits/char */
323f542974SPaul B Schroeder #define LCR_BITS_7             0x02	/* 7 bits/char */
333f542974SPaul B Schroeder #define LCR_BITS_8             0x03	/* 8 bits/char */
343f542974SPaul B Schroeder #define LCR_BITS_MASK          0x03	/* Mask for bits/char field */
353f542974SPaul B Schroeder 
363f542974SPaul B Schroeder #define LCR_STOP_1             0x00	/* 1 stop bit */
373f542974SPaul B Schroeder #define LCR_STOP_1_5           0x04	/* 1.5 stop bits (if 5   bits/char) */
383f542974SPaul B Schroeder #define LCR_STOP_2             0x04	/* 2 stop bits   (if 6-8 bits/char) */
393f542974SPaul B Schroeder #define LCR_STOP_MASK          0x04	/* Mask for stop bits field */
403f542974SPaul B Schroeder 
413f542974SPaul B Schroeder #define LCR_PAR_NONE           0x00	/* No parity */
423f542974SPaul B Schroeder #define LCR_PAR_ODD            0x08	/* Odd parity */
433f542974SPaul B Schroeder #define LCR_PAR_EVEN           0x18	/* Even parity */
443f542974SPaul B Schroeder #define LCR_PAR_MARK           0x28	/* Force parity bit to 1 */
453f542974SPaul B Schroeder #define LCR_PAR_SPACE          0x38	/* Force parity bit to 0 */
463f542974SPaul B Schroeder #define LCR_PAR_MASK           0x38	/* Mask for parity field */
473f542974SPaul B Schroeder 
483f542974SPaul B Schroeder #define LCR_SET_BREAK          0x40	/* Set Break condition */
493f542974SPaul B Schroeder #define LCR_DL_ENABLE          0x80	/* Enable access to divisor latch */
503f542974SPaul B Schroeder 
513f542974SPaul B Schroeder #define MCR_DTR                0x01	/* Assert DTR */
523f542974SPaul B Schroeder #define MCR_RTS                0x02	/* Assert RTS */
533f542974SPaul B Schroeder #define MCR_OUT1               0x04	/* Loopback only: Sets state of RI */
543f542974SPaul B Schroeder #define MCR_MASTER_IE          0x08	/* Enable interrupt outputs */
553f542974SPaul B Schroeder #define MCR_LOOPBACK           0x10	/* Set internal (digital) loopback mode */
563f542974SPaul B Schroeder #define MCR_XON_ANY            0x20	/* Enable any char to exit XOFF mode */
573f542974SPaul B Schroeder 
583f542974SPaul B Schroeder #define MOS7840_MSR_CTS        0x10	/* Current state of CTS */
593f542974SPaul B Schroeder #define MOS7840_MSR_DSR        0x20	/* Current state of DSR */
603f542974SPaul B Schroeder #define MOS7840_MSR_RI         0x40	/* Current state of RI */
613f542974SPaul B Schroeder #define MOS7840_MSR_CD         0x80	/* Current state of CD */
623f542974SPaul B Schroeder 
633f542974SPaul B Schroeder /*
643f542974SPaul B Schroeder  * Defines used for sending commands to port
653f542974SPaul B Schroeder  */
663f542974SPaul B Schroeder 
671e658489SMark Ferrell #define MOS_WDR_TIMEOUT		5000	/* default urb timeout */
683f542974SPaul B Schroeder 
693f542974SPaul B Schroeder #define MOS_PORT1       0x0200
703f542974SPaul B Schroeder #define MOS_PORT2       0x0300
713f542974SPaul B Schroeder #define MOS_VENREG      0x0000
723f542974SPaul B Schroeder #define MOS_MAX_PORT	0x02
733f542974SPaul B Schroeder #define MOS_WRITE       0x0E
743f542974SPaul B Schroeder #define MOS_READ        0x0D
753f542974SPaul B Schroeder 
763f542974SPaul B Schroeder /* Requests */
773f542974SPaul B Schroeder #define MCS_RD_RTYPE    0xC0
783f542974SPaul B Schroeder #define MCS_WR_RTYPE    0x40
793f542974SPaul B Schroeder #define MCS_RDREQ       0x0D
803f542974SPaul B Schroeder #define MCS_WRREQ       0x0E
813f542974SPaul B Schroeder #define MCS_CTRL_TIMEOUT        500
823f542974SPaul B Schroeder #define VENDOR_READ_LENGTH      (0x01)
833f542974SPaul B Schroeder 
843f542974SPaul B Schroeder #define MAX_NAME_LEN    64
853f542974SPaul B Schroeder 
86880af9dbSAlan Cox #define ZLP_REG1  0x3A		/* Zero_Flag_Reg1    58 */
87880af9dbSAlan Cox #define ZLP_REG5  0x3E		/* Zero_Flag_Reg5    62 */
883f542974SPaul B Schroeder 
893f542974SPaul B Schroeder /* For higher baud Rates use TIOCEXBAUD */
903f542974SPaul B Schroeder #define TIOCEXBAUD     0x5462
913f542974SPaul B Schroeder 
92*375cb533SJohan Hovold /*
93*375cb533SJohan Hovold  * Vendor id and device id defines
94*375cb533SJohan Hovold  *
95*375cb533SJohan Hovold  * NOTE: Do not add new defines, add entries directly to the id_table instead.
9611e1abb4SDavid Ludlow  */
9711e1abb4SDavid Ludlow #define USB_VENDOR_ID_BANDB              0x0856
98acf509aeSCliff Brake #define BANDB_DEVICE_ID_USO9ML2_2        0xAC22
99870408c8SDave Ludlow #define BANDB_DEVICE_ID_USO9ML2_2P       0xBC00
100acf509aeSCliff Brake #define BANDB_DEVICE_ID_USO9ML2_4        0xAC24
101870408c8SDave Ludlow #define BANDB_DEVICE_ID_USO9ML2_4P       0xBC01
102acf509aeSCliff Brake #define BANDB_DEVICE_ID_US9ML2_2         0xAC29
103acf509aeSCliff Brake #define BANDB_DEVICE_ID_US9ML2_4         0xAC30
104acf509aeSCliff Brake #define BANDB_DEVICE_ID_USPTL4_2         0xAC31
105acf509aeSCliff Brake #define BANDB_DEVICE_ID_USPTL4_4         0xAC32
10611e1abb4SDavid Ludlow #define BANDB_DEVICE_ID_USOPTL4_2        0xAC42
107caf3a636SDave Ludlow #define BANDB_DEVICE_ID_USOPTL4_2P       0xBC02
108870408c8SDave Ludlow #define BANDB_DEVICE_ID_USOPTL4_4        0xAC44
109870408c8SDave Ludlow #define BANDB_DEVICE_ID_USOPTL4_4P       0xBC03
110870408c8SDave Ludlow #define BANDB_DEVICE_ID_USOPTL2_4        0xAC24
1113f542974SPaul B Schroeder 
11211e1abb4SDavid Ludlow /* Interrupt Routine Defines    */
1133f542974SPaul B Schroeder 
1143f542974SPaul B Schroeder #define SERIAL_IIR_RLS      0x06
1153f542974SPaul B Schroeder #define SERIAL_IIR_MS       0x00
1163f542974SPaul B Schroeder 
1173f542974SPaul B Schroeder /*
1183f542974SPaul B Schroeder  *  Emulation of the bit mask on the LINE STATUS REGISTER.
1193f542974SPaul B Schroeder  */
1203f542974SPaul B Schroeder #define SERIAL_LSR_DR       0x0001
1213f542974SPaul B Schroeder #define SERIAL_LSR_OE       0x0002
1223f542974SPaul B Schroeder #define SERIAL_LSR_PE       0x0004
1233f542974SPaul B Schroeder #define SERIAL_LSR_FE       0x0008
1243f542974SPaul B Schroeder #define SERIAL_LSR_BI       0x0010
1253f542974SPaul B Schroeder 
1263f542974SPaul B Schroeder #define MOS_MSR_DELTA_CTS   0x10
1273f542974SPaul B Schroeder #define MOS_MSR_DELTA_DSR   0x20
1283f542974SPaul B Schroeder #define MOS_MSR_DELTA_RI    0x40
1293f542974SPaul B Schroeder #define MOS_MSR_DELTA_CD    0x80
1303f542974SPaul B Schroeder 
131880af9dbSAlan Cox /* Serial Port register Address */
1323f542974SPaul B Schroeder #define INTERRUPT_ENABLE_REGISTER  ((__u16)(0x01))
1333f542974SPaul B Schroeder #define FIFO_CONTROL_REGISTER      ((__u16)(0x02))
1343f542974SPaul B Schroeder #define LINE_CONTROL_REGISTER      ((__u16)(0x03))
1353f542974SPaul B Schroeder #define MODEM_CONTROL_REGISTER     ((__u16)(0x04))
1363f542974SPaul B Schroeder #define LINE_STATUS_REGISTER       ((__u16)(0x05))
1373f542974SPaul B Schroeder #define MODEM_STATUS_REGISTER      ((__u16)(0x06))
1383f542974SPaul B Schroeder #define SCRATCH_PAD_REGISTER       ((__u16)(0x07))
1393f542974SPaul B Schroeder #define DIVISOR_LATCH_LSB          ((__u16)(0x00))
1403f542974SPaul B Schroeder #define DIVISOR_LATCH_MSB          ((__u16)(0x01))
1413f542974SPaul B Schroeder 
1423f542974SPaul B Schroeder #define CLK_MULTI_REGISTER         ((__u16)(0x02))
1433f542974SPaul B Schroeder #define CLK_START_VALUE_REGISTER   ((__u16)(0x03))
144093ea2d3SDonald Lee #define GPIO_REGISTER              ((__u16)(0x07))
1453f542974SPaul B Schroeder 
1463f542974SPaul B Schroeder #define SERIAL_LCR_DLAB            ((__u16)(0x0080))
1473f542974SPaul B Schroeder 
1483f542974SPaul B Schroeder /*
1493f542974SPaul B Schroeder  * URB POOL related defines
1503f542974SPaul B Schroeder  */
1513f542974SPaul B Schroeder #define NUM_URBS                        16	/* URB Count */
1523f542974SPaul B Schroeder #define URB_TRANSFER_BUFFER_SIZE        32	/* URB Size  */
1533f542974SPaul B Schroeder 
1540eafe4deSDonald /* LED on/off milliseconds*/
1550eafe4deSDonald #define LED_ON_MS	500
1560eafe4deSDonald #define LED_OFF_MS	500
1570eafe4deSDonald 
158d8a083ccSJohan Hovold enum mos7840_flag {
159d8a083ccSJohan Hovold 	MOS7840_FLAG_CTRL_BUSY,
16005cf0decSJohan Hovold 	MOS7840_FLAG_LED_BUSY,
161d8a083ccSJohan Hovold };
1623f542974SPaul B Schroeder 
163*375cb533SJohan Hovold #define MCS_PORT_MASK	GENMASK(2, 0)
164*375cb533SJohan Hovold #define MCS_PORTS(nr)	((nr) & MCS_PORT_MASK)
165*375cb533SJohan Hovold #define MCS_LED		BIT(3)
166*375cb533SJohan Hovold 
167*375cb533SJohan Hovold #define MCS_DEVICE(vid, pid, flags) \
168*375cb533SJohan Hovold 		USB_DEVICE((vid), (pid)), .driver_info = (flags)
169*375cb533SJohan Hovold 
170b9c87663STony Zelenoff static const struct usb_device_id id_table[] = {
171*375cb533SJohan Hovold 	{ MCS_DEVICE(0x0557, 0x2011, MCS_PORTS(4)) },	/* ATEN UC2324 */
172*375cb533SJohan Hovold 	{ MCS_DEVICE(0x0557, 0x7820, MCS_PORTS(2)) },	/* ATEN UC2322 */
173*375cb533SJohan Hovold 	{ MCS_DEVICE(0x110a, 0x2210, MCS_PORTS(2)) },	/* Moxa UPort 2210 */
174*375cb533SJohan Hovold 	{ MCS_DEVICE(0x9710, 0x7810, MCS_PORTS(1) | MCS_LED) }, /* ASIX MCS7810 */
175*375cb533SJohan Hovold 	{ MCS_DEVICE(0x9710, 0x7820, MCS_PORTS(2)) },	/* MosChip MCS7820 */
176*375cb533SJohan Hovold 	{ MCS_DEVICE(0x9710, 0x7840, MCS_PORTS(4)) },	/* MosChip MCS7840 */
177*375cb533SJohan Hovold 	{ MCS_DEVICE(0x9710, 0x7843, MCS_PORTS(3)) },	/* ASIX MCS7840 3 port */
178acf509aeSCliff Brake 	{ USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_2) },
179870408c8SDave Ludlow 	{ USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_2P) },
180acf509aeSCliff Brake 	{ USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_4) },
181870408c8SDave Ludlow 	{ USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_4P) },
182acf509aeSCliff Brake 	{ USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_US9ML2_2) },
183acf509aeSCliff Brake 	{ USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_US9ML2_4) },
184acf509aeSCliff Brake 	{ USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USPTL4_2) },
185acf509aeSCliff Brake 	{ USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USPTL4_4) },
18611e1abb4SDavid Ludlow 	{ USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_2) },
187870408c8SDave Ludlow 	{ USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_2P) },
188acf509aeSCliff Brake 	{ USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_4) },
189870408c8SDave Ludlow 	{ USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_4P) },
19027f1281dSBlaise Gassend 	{ USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL2_4) },
1913f542974SPaul B Schroeder 	{}			/* terminating entry */
1923f542974SPaul B Schroeder };
19368e24113SGreg Kroah-Hartman MODULE_DEVICE_TABLE(usb, id_table);
1943f542974SPaul B Schroeder 
1953f542974SPaul B Schroeder /* This structure holds all of the local port information */
1963f542974SPaul B Schroeder 
1973f542974SPaul B Schroeder struct moschip_port {
1983f542974SPaul B Schroeder 	int port_num;		/*Actual port number in the device(1,2,etc) */
1993f542974SPaul B Schroeder 	struct urb *read_urb;	/* read URB for this port */
2003f542974SPaul B Schroeder 	__u8 shadowLCR;		/* last LCR value received */
2013f542974SPaul B Schroeder 	__u8 shadowMCR;		/* last MCR value received */
2023f542974SPaul B Schroeder 	char open;
2030de9a702SOliver Neukum 	char open_ports;
2043f542974SPaul B Schroeder 	struct usb_serial_port *port;	/* loop back to the owner of this object */
2053f542974SPaul B Schroeder 
2063f542974SPaul B Schroeder 	/* Offsets */
2073f542974SPaul B Schroeder 	__u8 SpRegOffset;
2083f542974SPaul B Schroeder 	__u8 ControlRegOffset;
2093f542974SPaul B Schroeder 	__u8 DcrRegOffset;
210880af9dbSAlan Cox 	/* for processing control URBS in interrupt context */
2113f542974SPaul B Schroeder 	struct urb *control_urb;
2120de9a702SOliver Neukum 	struct usb_ctrlrequest *dr;
2133f542974SPaul B Schroeder 	char *ctrl_buf;
2143f542974SPaul B Schroeder 	int MsrLsr;
2153f542974SPaul B Schroeder 
2160de9a702SOliver Neukum 	spinlock_t pool_lock;
2173f542974SPaul B Schroeder 	struct urb *write_urb_pool[NUM_URBS];
2180de9a702SOliver Neukum 	char busy[NUM_URBS];
21950de36f7SGreg Kroah-Hartman 	bool read_urb_busy;
2203f542974SPaul B Schroeder 
2210eafe4deSDonald 	/* For device(s) with LED indicator */
2220eafe4deSDonald 	bool has_led;
2230eafe4deSDonald 	struct timer_list led_timer1;	/* Timer for LED on */
2240eafe4deSDonald 	struct timer_list led_timer2;	/* Timer for LED off */
22505cf0decSJohan Hovold 	struct urb *led_urb;
22605cf0decSJohan Hovold 	struct usb_ctrlrequest *led_dr;
227d8a083ccSJohan Hovold 
228d8a083ccSJohan Hovold 	unsigned long flags;
2290eafe4deSDonald };
2303f542974SPaul B Schroeder 
2313f542974SPaul B Schroeder /*
2323f542974SPaul B Schroeder  * mos7840_set_reg_sync
2333f542974SPaul B Schroeder  * 	To set the Control register by calling usb_fill_control_urb function
2343f542974SPaul B Schroeder  *	by passing usb_sndctrlpipe function as parameter.
2353f542974SPaul B Schroeder  */
2363f542974SPaul B Schroeder 
2373f542974SPaul B Schroeder static int mos7840_set_reg_sync(struct usb_serial_port *port, __u16 reg,
2383f542974SPaul B Schroeder 				__u16 val)
2393f542974SPaul B Schroeder {
2403f542974SPaul B Schroeder 	struct usb_device *dev = port->serial->dev;
2413f542974SPaul B Schroeder 	val = val & 0x00ff;
2429c134a14SGreg Kroah-Hartman 	dev_dbg(&port->dev, "mos7840_set_reg_sync offset is %x, value %x\n", reg, val);
2433f542974SPaul B Schroeder 
2443f542974SPaul B Schroeder 	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), MCS_WRREQ,
2453f542974SPaul B Schroeder 			       MCS_WR_RTYPE, val, reg, NULL, 0,
2463f542974SPaul B Schroeder 			       MOS_WDR_TIMEOUT);
2473f542974SPaul B Schroeder }
2483f542974SPaul B Schroeder 
2493f542974SPaul B Schroeder /*
2503f542974SPaul B Schroeder  * mos7840_get_reg_sync
2513f542974SPaul B Schroeder  * 	To set the Uart register by calling usb_fill_control_urb function by
2523f542974SPaul B Schroeder  *	passing usb_rcvctrlpipe function as parameter.
2533f542974SPaul B Schroeder  */
2543f542974SPaul B Schroeder 
2553f542974SPaul B Schroeder static int mos7840_get_reg_sync(struct usb_serial_port *port, __u16 reg,
2563f542974SPaul B Schroeder 				__u16 *val)
2573f542974SPaul B Schroeder {
2583f542974SPaul B Schroeder 	struct usb_device *dev = port->serial->dev;
2593f542974SPaul B Schroeder 	int ret = 0;
2609e221a35SJohan Hovold 	u8 *buf;
2619e221a35SJohan Hovold 
2629e221a35SJohan Hovold 	buf = kmalloc(VENDOR_READ_LENGTH, GFP_KERNEL);
2639e221a35SJohan Hovold 	if (!buf)
2649e221a35SJohan Hovold 		return -ENOMEM;
2653f542974SPaul B Schroeder 
2663f542974SPaul B Schroeder 	ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ,
2679e221a35SJohan Hovold 			      MCS_RD_RTYPE, 0, reg, buf, VENDOR_READ_LENGTH,
2683f542974SPaul B Schroeder 			      MOS_WDR_TIMEOUT);
269cd8db057SJohan Hovold 	if (ret < VENDOR_READ_LENGTH) {
270cd8db057SJohan Hovold 		if (ret >= 0)
271cd8db057SJohan Hovold 			ret = -EIO;
272cd8db057SJohan Hovold 		goto out;
273cd8db057SJohan Hovold 	}
274cd8db057SJohan Hovold 
2759e221a35SJohan Hovold 	*val = buf[0];
2769c134a14SGreg Kroah-Hartman 	dev_dbg(&port->dev, "%s offset is %x, return val %x\n", __func__, reg, *val);
277cd8db057SJohan Hovold out:
2789e221a35SJohan Hovold 	kfree(buf);
2793f542974SPaul B Schroeder 	return ret;
2803f542974SPaul B Schroeder }
2813f542974SPaul B Schroeder 
2823f542974SPaul B Schroeder /*
2833f542974SPaul B Schroeder  * mos7840_set_uart_reg
2843f542974SPaul B Schroeder  *	To set the Uart register by calling usb_fill_control_urb function by
2853f542974SPaul B Schroeder  *	passing usb_sndctrlpipe function as parameter.
2863f542974SPaul B Schroeder  */
2873f542974SPaul B Schroeder 
2883f542974SPaul B Schroeder static int mos7840_set_uart_reg(struct usb_serial_port *port, __u16 reg,
2893f542974SPaul B Schroeder 				__u16 val)
2903f542974SPaul B Schroeder {
2913f542974SPaul B Schroeder 
2923f542974SPaul B Schroeder 	struct usb_device *dev = port->serial->dev;
2933f542974SPaul B Schroeder 	val = val & 0x00ff;
294880af9dbSAlan Cox 	/* For the UART control registers, the application number need
295880af9dbSAlan Cox 	   to be Or'ed */
296e8603076SJackyChou 	if (port->serial->num_ports == 2 && port->port_number != 0)
2971143832eSGreg Kroah-Hartman 		val |= ((__u16)port->port_number + 2) << 8;
298e8603076SJackyChou 	else
299e8603076SJackyChou 		val |= ((__u16)port->port_number + 1) << 8;
3009c134a14SGreg Kroah-Hartman 	dev_dbg(&port->dev, "%s application number is %x\n", __func__, val);
3013f542974SPaul B Schroeder 	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), MCS_WRREQ,
3023f542974SPaul B Schroeder 			       MCS_WR_RTYPE, val, reg, NULL, 0,
3033f542974SPaul B Schroeder 			       MOS_WDR_TIMEOUT);
3043f542974SPaul B Schroeder 
3053f542974SPaul B Schroeder }
3063f542974SPaul B Schroeder 
3073f542974SPaul B Schroeder /*
3083f542974SPaul B Schroeder  * mos7840_get_uart_reg
3093f542974SPaul B Schroeder  *	To set the Control register by calling usb_fill_control_urb function
3103f542974SPaul B Schroeder  *	by passing usb_rcvctrlpipe function as parameter.
3113f542974SPaul B Schroeder  */
3123f542974SPaul B Schroeder static int mos7840_get_uart_reg(struct usb_serial_port *port, __u16 reg,
3133f542974SPaul B Schroeder 				__u16 *val)
3143f542974SPaul B Schroeder {
3153f542974SPaul B Schroeder 	struct usb_device *dev = port->serial->dev;
3163f542974SPaul B Schroeder 	int ret = 0;
3173f542974SPaul B Schroeder 	__u16 Wval;
3189e221a35SJohan Hovold 	u8 *buf;
3199e221a35SJohan Hovold 
3209e221a35SJohan Hovold 	buf = kmalloc(VENDOR_READ_LENGTH, GFP_KERNEL);
3219e221a35SJohan Hovold 	if (!buf)
3229e221a35SJohan Hovold 		return -ENOMEM;
3233f542974SPaul B Schroeder 
3243f542974SPaul B Schroeder 	/* Wval  is same as application number */
325e8603076SJackyChou 	if (port->serial->num_ports == 2 && port->port_number != 0)
3261143832eSGreg Kroah-Hartman 		Wval = ((__u16)port->port_number + 2) << 8;
327e8603076SJackyChou 	else
328e8603076SJackyChou 		Wval = ((__u16)port->port_number + 1) << 8;
3299c134a14SGreg Kroah-Hartman 	dev_dbg(&port->dev, "%s application number is %x\n", __func__, Wval);
3303f542974SPaul B Schroeder 	ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ,
3319e221a35SJohan Hovold 			      MCS_RD_RTYPE, Wval, reg, buf, VENDOR_READ_LENGTH,
3323f542974SPaul B Schroeder 			      MOS_WDR_TIMEOUT);
333cd8db057SJohan Hovold 	if (ret < VENDOR_READ_LENGTH) {
334cd8db057SJohan Hovold 		if (ret >= 0)
335cd8db057SJohan Hovold 			ret = -EIO;
336cd8db057SJohan Hovold 		goto out;
337cd8db057SJohan Hovold 	}
3389e221a35SJohan Hovold 	*val = buf[0];
339cd8db057SJohan Hovold out:
3409e221a35SJohan Hovold 	kfree(buf);
3413f542974SPaul B Schroeder 	return ret;
3423f542974SPaul B Schroeder }
3433f542974SPaul B Schroeder 
3449c134a14SGreg Kroah-Hartman static void mos7840_dump_serial_port(struct usb_serial_port *port,
3459c134a14SGreg Kroah-Hartman 				     struct moschip_port *mos7840_port)
3463f542974SPaul B Schroeder {
3473f542974SPaul B Schroeder 
3489c134a14SGreg Kroah-Hartman 	dev_dbg(&port->dev, "SpRegOffset is %2x\n", mos7840_port->SpRegOffset);
3499c134a14SGreg Kroah-Hartman 	dev_dbg(&port->dev, "ControlRegOffset is %2x\n", mos7840_port->ControlRegOffset);
3509c134a14SGreg Kroah-Hartman 	dev_dbg(&port->dev, "DCRRegOffset is %2x\n", mos7840_port->DcrRegOffset);
3513f542974SPaul B Schroeder 
3523f542974SPaul B Schroeder }
3533f542974SPaul B Schroeder 
3543f542974SPaul B Schroeder /************************************************************************/
3553f542974SPaul B Schroeder /************************************************************************/
3563f542974SPaul B Schroeder /*             I N T E R F A C E   F U N C T I O N S			*/
3573f542974SPaul B Schroeder /*             I N T E R F A C E   F U N C T I O N S			*/
3583f542974SPaul B Schroeder /************************************************************************/
3593f542974SPaul B Schroeder /************************************************************************/
3603f542974SPaul B Schroeder 
3613f542974SPaul B Schroeder static inline void mos7840_set_port_private(struct usb_serial_port *port,
3623f542974SPaul B Schroeder 					    struct moschip_port *data)
3633f542974SPaul B Schroeder {
3643f542974SPaul B Schroeder 	usb_set_serial_port_data(port, (void *)data);
3653f542974SPaul B Schroeder }
3663f542974SPaul B Schroeder 
3673f542974SPaul B Schroeder static inline struct moschip_port *mos7840_get_port_private(struct
3683f542974SPaul B Schroeder 							    usb_serial_port
3693f542974SPaul B Schroeder 							    *port)
3703f542974SPaul B Schroeder {
3713f542974SPaul B Schroeder 	return (struct moschip_port *)usb_get_serial_port_data(port);
3723f542974SPaul B Schroeder }
3733f542974SPaul B Schroeder 
3740de9a702SOliver Neukum static void mos7840_handle_new_msr(struct moschip_port *port, __u8 new_msr)
3753f542974SPaul B Schroeder {
3763f542974SPaul B Schroeder 	struct moschip_port *mos7840_port;
3773f542974SPaul B Schroeder 	struct async_icount *icount;
3783f542974SPaul B Schroeder 	mos7840_port = port;
3793f542974SPaul B Schroeder 	if (new_msr &
3803f542974SPaul B Schroeder 	    (MOS_MSR_DELTA_CTS | MOS_MSR_DELTA_DSR | MOS_MSR_DELTA_RI |
3813f542974SPaul B Schroeder 	     MOS_MSR_DELTA_CD)) {
3828c1a07ffSJohan Hovold 		icount = &mos7840_port->port->icount;
3833f542974SPaul B Schroeder 
3843f542974SPaul B Schroeder 		/* update input line counters */
385c9fac853SJohan Hovold 		if (new_msr & MOS_MSR_DELTA_CTS)
3863f542974SPaul B Schroeder 			icount->cts++;
387c9fac853SJohan Hovold 		if (new_msr & MOS_MSR_DELTA_DSR)
3883f542974SPaul B Schroeder 			icount->dsr++;
389c9fac853SJohan Hovold 		if (new_msr & MOS_MSR_DELTA_CD)
3903f542974SPaul B Schroeder 			icount->dcd++;
391c9fac853SJohan Hovold 		if (new_msr & MOS_MSR_DELTA_RI)
3923f542974SPaul B Schroeder 			icount->rng++;
393e670c6afSJohan Hovold 
3940c613371SJohan Hovold 		wake_up_interruptible(&port->port->port.delta_msr_wait);
3953f542974SPaul B Schroeder 	}
3963f542974SPaul B Schroeder }
3973f542974SPaul B Schroeder 
3980de9a702SOliver Neukum static void mos7840_handle_new_lsr(struct moschip_port *port, __u8 new_lsr)
3993f542974SPaul B Schroeder {
4003f542974SPaul B Schroeder 	struct async_icount *icount;
4013f542974SPaul B Schroeder 
4023f542974SPaul B Schroeder 	if (new_lsr & SERIAL_LSR_BI) {
403880af9dbSAlan Cox 		/*
404880af9dbSAlan Cox 		 * Parity and Framing errors only count if they
405880af9dbSAlan Cox 		 * occur exclusive of a break being
406880af9dbSAlan Cox 		 * received.
407880af9dbSAlan Cox 		 */
4083f542974SPaul B Schroeder 		new_lsr &= (__u8) (SERIAL_LSR_OE | SERIAL_LSR_BI);
4093f542974SPaul B Schroeder 	}
4103f542974SPaul B Schroeder 
4113f542974SPaul B Schroeder 	/* update input line counters */
4128c1a07ffSJohan Hovold 	icount = &port->port->icount;
413c9fac853SJohan Hovold 	if (new_lsr & SERIAL_LSR_BI)
4143f542974SPaul B Schroeder 		icount->brk++;
415c9fac853SJohan Hovold 	if (new_lsr & SERIAL_LSR_OE)
4163f542974SPaul B Schroeder 		icount->overrun++;
417c9fac853SJohan Hovold 	if (new_lsr & SERIAL_LSR_PE)
4183f542974SPaul B Schroeder 		icount->parity++;
419c9fac853SJohan Hovold 	if (new_lsr & SERIAL_LSR_FE)
4203f542974SPaul B Schroeder 		icount->frame++;
4213f542974SPaul B Schroeder }
4223f542974SPaul B Schroeder 
4233f542974SPaul B Schroeder /************************************************************************/
4243f542974SPaul B Schroeder /************************************************************************/
4253f542974SPaul B Schroeder /*            U S B  C A L L B A C K   F U N C T I O N S                */
4263f542974SPaul B Schroeder /*            U S B  C A L L B A C K   F U N C T I O N S                */
4273f542974SPaul B Schroeder /************************************************************************/
4283f542974SPaul B Schroeder /************************************************************************/
4293f542974SPaul B Schroeder 
4307d12e780SDavid Howells static void mos7840_control_callback(struct urb *urb)
4313f542974SPaul B Schroeder {
4323f542974SPaul B Schroeder 	unsigned char *data;
4333f542974SPaul B Schroeder 	struct moschip_port *mos7840_port;
4349c134a14SGreg Kroah-Hartman 	struct device *dev = &urb->dev->dev;
4353f542974SPaul B Schroeder 	__u8 regval = 0x0;
4360643c724SGreg Kroah-Hartman 	int status = urb->status;
4373f542974SPaul B Schroeder 
438cdc97792SMing Lei 	mos7840_port = urb->context;
4390de9a702SOliver Neukum 
4400643c724SGreg Kroah-Hartman 	switch (status) {
4413f542974SPaul B Schroeder 	case 0:
4423f542974SPaul B Schroeder 		/* success */
4433f542974SPaul B Schroeder 		break;
4443f542974SPaul B Schroeder 	case -ECONNRESET:
4453f542974SPaul B Schroeder 	case -ENOENT:
4463f542974SPaul B Schroeder 	case -ESHUTDOWN:
4473f542974SPaul B Schroeder 		/* this urb is terminated, clean up */
4489c134a14SGreg Kroah-Hartman 		dev_dbg(dev, "%s - urb shutting down with status: %d\n", __func__, status);
449d8a083ccSJohan Hovold 		goto out;
4503f542974SPaul B Schroeder 	default:
4519c134a14SGreg Kroah-Hartman 		dev_dbg(dev, "%s - nonzero urb status received: %d\n", __func__, status);
452d8a083ccSJohan Hovold 		goto out;
4533f542974SPaul B Schroeder 	}
4543f542974SPaul B Schroeder 
4559c134a14SGreg Kroah-Hartman 	dev_dbg(dev, "%s urb buffer size is %d\n", __func__, urb->actual_length);
456794744abSJohan Hovold 	if (urb->actual_length < 1)
457794744abSJohan Hovold 		goto out;
458794744abSJohan Hovold 
4599c134a14SGreg Kroah-Hartman 	dev_dbg(dev, "%s mos7840_port->MsrLsr is %d port %d\n", __func__,
4603f542974SPaul B Schroeder 		mos7840_port->MsrLsr, mos7840_port->port_num);
4613f542974SPaul B Schroeder 	data = urb->transfer_buffer;
4623f542974SPaul B Schroeder 	regval = (__u8) data[0];
4639c134a14SGreg Kroah-Hartman 	dev_dbg(dev, "%s data is %x\n", __func__, regval);
4643f542974SPaul B Schroeder 	if (mos7840_port->MsrLsr == 0)
4653f542974SPaul B Schroeder 		mos7840_handle_new_msr(mos7840_port, regval);
4663f542974SPaul B Schroeder 	else if (mos7840_port->MsrLsr == 1)
4673f542974SPaul B Schroeder 		mos7840_handle_new_lsr(mos7840_port, regval);
468d8a083ccSJohan Hovold out:
469d8a083ccSJohan Hovold 	clear_bit_unlock(MOS7840_FLAG_CTRL_BUSY, &mos7840_port->flags);
4703f542974SPaul B Schroeder }
4713f542974SPaul B Schroeder 
4723f542974SPaul B Schroeder static int mos7840_get_reg(struct moschip_port *mcs, __u16 Wval, __u16 reg,
4733f542974SPaul B Schroeder 			   __u16 *val)
4743f542974SPaul B Schroeder {
4753f542974SPaul B Schroeder 	struct usb_device *dev = mcs->port->serial->dev;
4760de9a702SOliver Neukum 	struct usb_ctrlrequest *dr = mcs->dr;
4770de9a702SOliver Neukum 	unsigned char *buffer = mcs->ctrl_buf;
4780de9a702SOliver Neukum 	int ret;
4793f542974SPaul B Schroeder 
480d8a083ccSJohan Hovold 	if (test_and_set_bit_lock(MOS7840_FLAG_CTRL_BUSY, &mcs->flags))
481d8a083ccSJohan Hovold 		return -EBUSY;
482d8a083ccSJohan Hovold 
4833f542974SPaul B Schroeder 	dr->bRequestType = MCS_RD_RTYPE;
4843f542974SPaul B Schroeder 	dr->bRequest = MCS_RDREQ;
485880af9dbSAlan Cox 	dr->wValue = cpu_to_le16(Wval);	/* 0 */
4863f542974SPaul B Schroeder 	dr->wIndex = cpu_to_le16(reg);
4873f542974SPaul B Schroeder 	dr->wLength = cpu_to_le16(2);
4883f542974SPaul B Schroeder 
4893f542974SPaul B Schroeder 	usb_fill_control_urb(mcs->control_urb, dev, usb_rcvctrlpipe(dev, 0),
4903f542974SPaul B Schroeder 			     (unsigned char *)dr, buffer, 2,
4913f542974SPaul B Schroeder 			     mos7840_control_callback, mcs);
4923f542974SPaul B Schroeder 	mcs->control_urb->transfer_buffer_length = 2;
4933f542974SPaul B Schroeder 	ret = usb_submit_urb(mcs->control_urb, GFP_ATOMIC);
494d8a083ccSJohan Hovold 	if (ret)
495d8a083ccSJohan Hovold 		clear_bit_unlock(MOS7840_FLAG_CTRL_BUSY, &mcs->flags);
496d8a083ccSJohan Hovold 
4973f542974SPaul B Schroeder 	return ret;
4983f542974SPaul B Schroeder }
4993f542974SPaul B Schroeder 
5000eafe4deSDonald static void mos7840_set_led_callback(struct urb *urb)
5010eafe4deSDonald {
5020eafe4deSDonald 	switch (urb->status) {
5030eafe4deSDonald 	case 0:
5040eafe4deSDonald 		/* Success */
5050eafe4deSDonald 		break;
5060eafe4deSDonald 	case -ECONNRESET:
5070eafe4deSDonald 	case -ENOENT:
5080eafe4deSDonald 	case -ESHUTDOWN:
5090eafe4deSDonald 		/* This urb is terminated, clean up */
510d9a38a87SJohan Hovold 		dev_dbg(&urb->dev->dev, "%s - urb shutting down: %d\n",
5119c134a14SGreg Kroah-Hartman 			__func__, urb->status);
5120eafe4deSDonald 		break;
5130eafe4deSDonald 	default:
514d9a38a87SJohan Hovold 		dev_dbg(&urb->dev->dev, "%s - nonzero urb status: %d\n",
5159c134a14SGreg Kroah-Hartman 			__func__, urb->status);
5160eafe4deSDonald 	}
5170eafe4deSDonald }
5180eafe4deSDonald 
5190eafe4deSDonald static void mos7840_set_led_async(struct moschip_port *mcs, __u16 wval,
5200eafe4deSDonald 				__u16 reg)
5210eafe4deSDonald {
5220eafe4deSDonald 	struct usb_device *dev = mcs->port->serial->dev;
52305cf0decSJohan Hovold 	struct usb_ctrlrequest *dr = mcs->led_dr;
5240eafe4deSDonald 
5250eafe4deSDonald 	dr->bRequestType = MCS_WR_RTYPE;
5260eafe4deSDonald 	dr->bRequest = MCS_WRREQ;
5270eafe4deSDonald 	dr->wValue = cpu_to_le16(wval);
5280eafe4deSDonald 	dr->wIndex = cpu_to_le16(reg);
5290eafe4deSDonald 	dr->wLength = cpu_to_le16(0);
5300eafe4deSDonald 
53105cf0decSJohan Hovold 	usb_fill_control_urb(mcs->led_urb, dev, usb_sndctrlpipe(dev, 0),
5320eafe4deSDonald 		(unsigned char *)dr, NULL, 0, mos7840_set_led_callback, NULL);
5330eafe4deSDonald 
53405cf0decSJohan Hovold 	usb_submit_urb(mcs->led_urb, GFP_ATOMIC);
5350eafe4deSDonald }
5360eafe4deSDonald 
5370eafe4deSDonald static void mos7840_set_led_sync(struct usb_serial_port *port, __u16 reg,
5380eafe4deSDonald 				__u16 val)
5390eafe4deSDonald {
5400eafe4deSDonald 	struct usb_device *dev = port->serial->dev;
5410eafe4deSDonald 
5420eafe4deSDonald 	usb_control_msg(dev, usb_sndctrlpipe(dev, 0), MCS_WRREQ, MCS_WR_RTYPE,
5430eafe4deSDonald 			val, reg, NULL, 0, MOS_WDR_TIMEOUT);
5440eafe4deSDonald }
5450eafe4deSDonald 
546e99e88a9SKees Cook static void mos7840_led_off(struct timer_list *t)
5470eafe4deSDonald {
548e99e88a9SKees Cook 	struct moschip_port *mcs = from_timer(mcs, t, led_timer1);
5490eafe4deSDonald 
5500eafe4deSDonald 	/* Turn off LED */
5510eafe4deSDonald 	mos7840_set_led_async(mcs, 0x0300, MODEM_CONTROL_REGISTER);
5520eafe4deSDonald 	mod_timer(&mcs->led_timer2,
5530eafe4deSDonald 				jiffies + msecs_to_jiffies(LED_OFF_MS));
5540eafe4deSDonald }
5550eafe4deSDonald 
556e99e88a9SKees Cook static void mos7840_led_flag_off(struct timer_list *t)
5570eafe4deSDonald {
558e99e88a9SKees Cook 	struct moschip_port *mcs = from_timer(mcs, t, led_timer2);
5590eafe4deSDonald 
56005cf0decSJohan Hovold 	clear_bit_unlock(MOS7840_FLAG_LED_BUSY, &mcs->flags);
56105cf0decSJohan Hovold }
56205cf0decSJohan Hovold 
56305cf0decSJohan Hovold static void mos7840_led_activity(struct usb_serial_port *port)
56405cf0decSJohan Hovold {
56505cf0decSJohan Hovold 	struct moschip_port *mos7840_port = usb_get_serial_port_data(port);
56605cf0decSJohan Hovold 
56705cf0decSJohan Hovold 	if (test_and_set_bit_lock(MOS7840_FLAG_LED_BUSY, &mos7840_port->flags))
56805cf0decSJohan Hovold 		return;
56905cf0decSJohan Hovold 
57005cf0decSJohan Hovold 	mos7840_set_led_async(mos7840_port, 0x0301, MODEM_CONTROL_REGISTER);
57105cf0decSJohan Hovold 	mod_timer(&mos7840_port->led_timer1,
57205cf0decSJohan Hovold 				jiffies + msecs_to_jiffies(LED_ON_MS));
5730eafe4deSDonald }
5740eafe4deSDonald 
5753f542974SPaul B Schroeder /*****************************************************************************
5763f542974SPaul B Schroeder  * mos7840_interrupt_callback
5773f542974SPaul B Schroeder  *	this is the callback function for when we have received data on the
5783f542974SPaul B Schroeder  *	interrupt endpoint.
5793f542974SPaul B Schroeder  *****************************************************************************/
5803f542974SPaul B Schroeder 
5817d12e780SDavid Howells static void mos7840_interrupt_callback(struct urb *urb)
5823f542974SPaul B Schroeder {
5833f542974SPaul B Schroeder 	int result;
5843f542974SPaul B Schroeder 	int length;
5853f542974SPaul B Schroeder 	struct moschip_port *mos7840_port;
5863f542974SPaul B Schroeder 	struct usb_serial *serial;
5873f542974SPaul B Schroeder 	__u16 Data;
5883f542974SPaul B Schroeder 	unsigned char *data;
58932d8a6fcSYueHaibing 	__u8 sp[5];
5900de9a702SOliver Neukum 	int i, rv = 0;
5910de9a702SOliver Neukum 	__u16 wval, wreg = 0;
5920643c724SGreg Kroah-Hartman 	int status = urb->status;
5933f542974SPaul B Schroeder 
5940643c724SGreg Kroah-Hartman 	switch (status) {
5953f542974SPaul B Schroeder 	case 0:
5963f542974SPaul B Schroeder 		/* success */
5973f542974SPaul B Schroeder 		break;
5983f542974SPaul B Schroeder 	case -ECONNRESET:
5993f542974SPaul B Schroeder 	case -ENOENT:
6003f542974SPaul B Schroeder 	case -ESHUTDOWN:
6013f542974SPaul B Schroeder 		/* this urb is terminated, clean up */
6029c134a14SGreg Kroah-Hartman 		dev_dbg(&urb->dev->dev, "%s - urb shutting down with status: %d\n",
6039c134a14SGreg Kroah-Hartman 			__func__, status);
6043f542974SPaul B Schroeder 		return;
6053f542974SPaul B Schroeder 	default:
6069c134a14SGreg Kroah-Hartman 		dev_dbg(&urb->dev->dev, "%s - nonzero urb status received: %d\n",
6079c134a14SGreg Kroah-Hartman 			__func__, status);
6083f542974SPaul B Schroeder 		goto exit;
6093f542974SPaul B Schroeder 	}
6103f542974SPaul B Schroeder 
6113f542974SPaul B Schroeder 	length = urb->actual_length;
6123f542974SPaul B Schroeder 	data = urb->transfer_buffer;
6133f542974SPaul B Schroeder 
614cdc97792SMing Lei 	serial = urb->context;
6153f542974SPaul B Schroeder 
6163f542974SPaul B Schroeder 	/* Moschip get 5 bytes
6173f542974SPaul B Schroeder 	 * Byte 1 IIR Port 1 (port.number is 0)
6183f542974SPaul B Schroeder 	 * Byte 2 IIR Port 2 (port.number is 1)
6193f542974SPaul B Schroeder 	 * Byte 3 IIR Port 3 (port.number is 2)
6203f542974SPaul B Schroeder 	 * Byte 4 IIR Port 4 (port.number is 3)
6213f542974SPaul B Schroeder 	 * Byte 5 FIFO status for both */
6223f542974SPaul B Schroeder 
623365a0442SGeyslan G. Bem 	if (length > 5) {
6249c134a14SGreg Kroah-Hartman 		dev_dbg(&urb->dev->dev, "%s", "Wrong data !!!\n");
6253f542974SPaul B Schroeder 		return;
6263f542974SPaul B Schroeder 	}
6273f542974SPaul B Schroeder 
6283f542974SPaul B Schroeder 	sp[0] = (__u8) data[0];
6293f542974SPaul B Schroeder 	sp[1] = (__u8) data[1];
6303f542974SPaul B Schroeder 	sp[2] = (__u8) data[2];
6313f542974SPaul B Schroeder 	sp[3] = (__u8) data[3];
6323f542974SPaul B Schroeder 
6333f542974SPaul B Schroeder 	for (i = 0; i < serial->num_ports; i++) {
6343f542974SPaul B Schroeder 		mos7840_port = mos7840_get_port_private(serial->port[i]);
6351143832eSGreg Kroah-Hartman 		wval = ((__u16)serial->port[i]->port_number + 1) << 8;
6363f542974SPaul B Schroeder 		if (mos7840_port->open) {
6373f542974SPaul B Schroeder 			if (sp[i] & 0x01) {
6389c134a14SGreg Kroah-Hartman 				dev_dbg(&urb->dev->dev, "SP%d No Interrupt !!!\n", i);
6393f542974SPaul B Schroeder 			} else {
6403f542974SPaul B Schroeder 				switch (sp[i] & 0x0f) {
6413f542974SPaul B Schroeder 				case SERIAL_IIR_RLS:
6429c134a14SGreg Kroah-Hartman 					dev_dbg(&urb->dev->dev, "Serial Port %d: Receiver status error or \n", i);
6439c134a14SGreg Kroah-Hartman 					dev_dbg(&urb->dev->dev, "address bit detected in 9-bit mode\n");
6443f542974SPaul B Schroeder 					mos7840_port->MsrLsr = 1;
6450de9a702SOliver Neukum 					wreg = LINE_STATUS_REGISTER;
6463f542974SPaul B Schroeder 					break;
6473f542974SPaul B Schroeder 				case SERIAL_IIR_MS:
6489c134a14SGreg Kroah-Hartman 					dev_dbg(&urb->dev->dev, "Serial Port %d: Modem status change\n", i);
6493f542974SPaul B Schroeder 					mos7840_port->MsrLsr = 0;
6500de9a702SOliver Neukum 					wreg = MODEM_STATUS_REGISTER;
6513f542974SPaul B Schroeder 					break;
6523f542974SPaul B Schroeder 				}
6530de9a702SOliver Neukum 				rv = mos7840_get_reg(mos7840_port, wval, wreg, &Data);
6543f542974SPaul B Schroeder 			}
6553f542974SPaul B Schroeder 		}
6563f542974SPaul B Schroeder 	}
657880af9dbSAlan Cox 	if (!(rv < 0))
658880af9dbSAlan Cox 		/* the completion handler for the control urb will resubmit */
6590de9a702SOliver Neukum 		return;
6603f542974SPaul B Schroeder exit:
6613f542974SPaul B Schroeder 	result = usb_submit_urb(urb, GFP_ATOMIC);
6623f542974SPaul B Schroeder 	if (result) {
6633f542974SPaul B Schroeder 		dev_err(&urb->dev->dev,
6643f542974SPaul B Schroeder 			"%s - Error %d submitting interrupt urb\n",
665441b62c1SHarvey Harrison 			__func__, result);
6663f542974SPaul B Schroeder 	}
6673f542974SPaul B Schroeder }
6683f542974SPaul B Schroeder 
6693f542974SPaul B Schroeder static int mos7840_port_paranoia_check(struct usb_serial_port *port,
6703f542974SPaul B Schroeder 				       const char *function)
6713f542974SPaul B Schroeder {
6723f542974SPaul B Schroeder 	if (!port) {
6739c134a14SGreg Kroah-Hartman 		pr_debug("%s - port == NULL\n", function);
6743f542974SPaul B Schroeder 		return -1;
6753f542974SPaul B Schroeder 	}
6763f542974SPaul B Schroeder 	if (!port->serial) {
6779c134a14SGreg Kroah-Hartman 		pr_debug("%s - port->serial == NULL\n", function);
6783f542974SPaul B Schroeder 		return -1;
6793f542974SPaul B Schroeder 	}
6803f542974SPaul B Schroeder 
6813f542974SPaul B Schroeder 	return 0;
6823f542974SPaul B Schroeder }
6833f542974SPaul B Schroeder 
6843f542974SPaul B Schroeder /* Inline functions to check the sanity of a pointer that is passed to us */
6853f542974SPaul B Schroeder static int mos7840_serial_paranoia_check(struct usb_serial *serial,
6863f542974SPaul B Schroeder 					 const char *function)
6873f542974SPaul B Schroeder {
6883f542974SPaul B Schroeder 	if (!serial) {
6899c134a14SGreg Kroah-Hartman 		pr_debug("%s - serial == NULL\n", function);
6903f542974SPaul B Schroeder 		return -1;
6913f542974SPaul B Schroeder 	}
6923f542974SPaul B Schroeder 	if (!serial->type) {
6939c134a14SGreg Kroah-Hartman 		pr_debug("%s - serial->type == NULL!\n", function);
6943f542974SPaul B Schroeder 		return -1;
6953f542974SPaul B Schroeder 	}
6963f542974SPaul B Schroeder 
6973f542974SPaul B Schroeder 	return 0;
6983f542974SPaul B Schroeder }
6993f542974SPaul B Schroeder 
7003f542974SPaul B Schroeder static struct usb_serial *mos7840_get_usb_serial(struct usb_serial_port *port,
7013f542974SPaul B Schroeder 						 const char *function)
7023f542974SPaul B Schroeder {
7033f542974SPaul B Schroeder 	/* if no port was specified, or it fails a paranoia check */
7043f542974SPaul B Schroeder 	if (!port ||
7053f542974SPaul B Schroeder 	    mos7840_port_paranoia_check(port, function) ||
7063f542974SPaul B Schroeder 	    mos7840_serial_paranoia_check(port->serial, function)) {
707880af9dbSAlan Cox 		/* then say that we don't have a valid usb_serial thing,
708880af9dbSAlan Cox 		 * which will end up genrating -ENODEV return values */
7093f542974SPaul B Schroeder 		return NULL;
7103f542974SPaul B Schroeder 	}
7113f542974SPaul B Schroeder 
7123f542974SPaul B Schroeder 	return port->serial;
7133f542974SPaul B Schroeder }
7143f542974SPaul B Schroeder 
7153f542974SPaul B Schroeder /*****************************************************************************
7163f542974SPaul B Schroeder  * mos7840_bulk_in_callback
7173f542974SPaul B Schroeder  *	this is the callback function for when we have received data on the
7183f542974SPaul B Schroeder  *	bulk in endpoint.
7193f542974SPaul B Schroeder  *****************************************************************************/
7203f542974SPaul B Schroeder 
7217d12e780SDavid Howells static void mos7840_bulk_in_callback(struct urb *urb)
7223f542974SPaul B Schroeder {
7230643c724SGreg Kroah-Hartman 	int retval;
7243f542974SPaul B Schroeder 	unsigned char *data;
7253f542974SPaul B Schroeder 	struct usb_serial *serial;
7263f542974SPaul B Schroeder 	struct usb_serial_port *port;
7273f542974SPaul B Schroeder 	struct moschip_port *mos7840_port;
7280643c724SGreg Kroah-Hartman 	int status = urb->status;
7293f542974SPaul B Schroeder 
730cdc97792SMing Lei 	mos7840_port = urb->context;
7319c134a14SGreg Kroah-Hartman 	if (!mos7840_port)
73250de36f7SGreg Kroah-Hartman 		return;
73350de36f7SGreg Kroah-Hartman 
73450de36f7SGreg Kroah-Hartman 	if (status) {
7359c134a14SGreg Kroah-Hartman 		dev_dbg(&urb->dev->dev, "nonzero read bulk status received: %d\n", status);
73650de36f7SGreg Kroah-Hartman 		mos7840_port->read_urb_busy = false;
7373f542974SPaul B Schroeder 		return;
7383f542974SPaul B Schroeder 	}
7393f542974SPaul B Schroeder 
7409c134a14SGreg Kroah-Hartman 	port = mos7840_port->port;
741441b62c1SHarvey Harrison 	if (mos7840_port_paranoia_check(port, __func__)) {
74250de36f7SGreg Kroah-Hartman 		mos7840_port->read_urb_busy = false;
7433f542974SPaul B Schroeder 		return;
7443f542974SPaul B Schroeder 	}
7453f542974SPaul B Schroeder 
746441b62c1SHarvey Harrison 	serial = mos7840_get_usb_serial(port, __func__);
7473f542974SPaul B Schroeder 	if (!serial) {
74850de36f7SGreg Kroah-Hartman 		mos7840_port->read_urb_busy = false;
7493f542974SPaul B Schroeder 		return;
7503f542974SPaul B Schroeder 	}
7513f542974SPaul B Schroeder 
7523f542974SPaul B Schroeder 	data = urb->transfer_buffer;
75359d33f2fSGreg Kroah-Hartman 	usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
7543f542974SPaul B Schroeder 
7553f542974SPaul B Schroeder 	if (urb->actual_length) {
75605c7cd39SJiri Slaby 		struct tty_port *tport = &mos7840_port->port->port;
75705c7cd39SJiri Slaby 		tty_insert_flip_string(tport, data, urb->actual_length);
7582e124b4aSJiri Slaby 		tty_flip_buffer_push(tport);
7598c1a07ffSJohan Hovold 		port->icount.rx += urb->actual_length;
7608c1a07ffSJohan Hovold 		dev_dbg(&port->dev, "icount.rx is %d:\n", port->icount.rx);
7613f542974SPaul B Schroeder 	}
7623f542974SPaul B Schroeder 
7633f542974SPaul B Schroeder 	if (!mos7840_port->read_urb) {
7649c134a14SGreg Kroah-Hartman 		dev_dbg(&port->dev, "%s", "URB KILLED !!!\n");
76550de36f7SGreg Kroah-Hartman 		mos7840_port->read_urb_busy = false;
7663f542974SPaul B Schroeder 		return;
7673f542974SPaul B Schroeder 	}
7683f542974SPaul B Schroeder 
76905cf0decSJohan Hovold 	if (mos7840_port->has_led)
77005cf0decSJohan Hovold 		mos7840_led_activity(port);
7710de9a702SOliver Neukum 
77250de36f7SGreg Kroah-Hartman 	mos7840_port->read_urb_busy = true;
7730643c724SGreg Kroah-Hartman 	retval = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC);
7743f542974SPaul B Schroeder 
7750643c724SGreg Kroah-Hartman 	if (retval) {
7769c134a14SGreg Kroah-Hartman 		dev_dbg(&port->dev, "usb_submit_urb(read bulk) failed, retval = %d\n", retval);
77750de36f7SGreg Kroah-Hartman 		mos7840_port->read_urb_busy = false;
7783f542974SPaul B Schroeder 	}
7793f542974SPaul B Schroeder }
7803f542974SPaul B Schroeder 
7813f542974SPaul B Schroeder /*****************************************************************************
7823f542974SPaul B Schroeder  * mos7840_bulk_out_data_callback
783880af9dbSAlan Cox  *	this is the callback function for when we have finished sending
784880af9dbSAlan Cox  *	serial data on the bulk out endpoint.
7853f542974SPaul B Schroeder  *****************************************************************************/
7863f542974SPaul B Schroeder 
7877d12e780SDavid Howells static void mos7840_bulk_out_data_callback(struct urb *urb)
7883f542974SPaul B Schroeder {
7893f542974SPaul B Schroeder 	struct moschip_port *mos7840_port;
7909c134a14SGreg Kroah-Hartman 	struct usb_serial_port *port;
7910643c724SGreg Kroah-Hartman 	int status = urb->status;
79219bfbf46SJohn Ogness 	unsigned long flags;
7930de9a702SOliver Neukum 	int i;
7940de9a702SOliver Neukum 
795cdc97792SMing Lei 	mos7840_port = urb->context;
7969c134a14SGreg Kroah-Hartman 	port = mos7840_port->port;
79719bfbf46SJohn Ogness 	spin_lock_irqsave(&mos7840_port->pool_lock, flags);
7980de9a702SOliver Neukum 	for (i = 0; i < NUM_URBS; i++) {
7990de9a702SOliver Neukum 		if (urb == mos7840_port->write_urb_pool[i]) {
8000de9a702SOliver Neukum 			mos7840_port->busy[i] = 0;
8010de9a702SOliver Neukum 			break;
8020de9a702SOliver Neukum 		}
8030de9a702SOliver Neukum 	}
80419bfbf46SJohn Ogness 	spin_unlock_irqrestore(&mos7840_port->pool_lock, flags);
8050de9a702SOliver Neukum 
8060643c724SGreg Kroah-Hartman 	if (status) {
8079c134a14SGreg Kroah-Hartman 		dev_dbg(&port->dev, "nonzero write bulk status received:%d\n", status);
8083f542974SPaul B Schroeder 		return;
8093f542974SPaul B Schroeder 	}
8103f542974SPaul B Schroeder 
8119c134a14SGreg Kroah-Hartman 	if (mos7840_port_paranoia_check(port, __func__))
8123f542974SPaul B Schroeder 		return;
8133f542974SPaul B Schroeder 
8146aad04f2SJiri Slaby 	if (mos7840_port->open)
8156aad04f2SJiri Slaby 		tty_port_tty_wakeup(&port->port);
8163f542974SPaul B Schroeder 
8173f542974SPaul B Schroeder }
8183f542974SPaul B Schroeder 
8193f542974SPaul B Schroeder /************************************************************************/
8203f542974SPaul B Schroeder /*       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       */
8213f542974SPaul B Schroeder /************************************************************************/
8223f542974SPaul B Schroeder 
8233f542974SPaul B Schroeder /*****************************************************************************
8243f542974SPaul B Schroeder  * mos7840_open
8253f542974SPaul B Schroeder  *	this function is called by the tty driver when a port is opened
8263f542974SPaul B Schroeder  *	If successful, we return 0
8273f542974SPaul B Schroeder  *	Otherwise we return a negative error number.
8283f542974SPaul B Schroeder  *****************************************************************************/
8293f542974SPaul B Schroeder 
830a509a7e4SAlan Cox static int mos7840_open(struct tty_struct *tty, struct usb_serial_port *port)
8313f542974SPaul B Schroeder {
8323f542974SPaul B Schroeder 	int response;
8333f542974SPaul B Schroeder 	int j;
8343f542974SPaul B Schroeder 	struct usb_serial *serial;
8353f542974SPaul B Schroeder 	struct urb *urb;
8363f542974SPaul B Schroeder 	__u16 Data;
8373f542974SPaul B Schroeder 	int status;
8383f542974SPaul B Schroeder 	struct moschip_port *mos7840_port;
8390de9a702SOliver Neukum 	struct moschip_port *port0;
8403f542974SPaul B Schroeder 
8419c134a14SGreg Kroah-Hartman 	if (mos7840_port_paranoia_check(port, __func__))
8423f542974SPaul B Schroeder 		return -ENODEV;
8433f542974SPaul B Schroeder 
8443f542974SPaul B Schroeder 	serial = port->serial;
8453f542974SPaul B Schroeder 
8469c134a14SGreg Kroah-Hartman 	if (mos7840_serial_paranoia_check(serial, __func__))
8473f542974SPaul B Schroeder 		return -ENODEV;
8483f542974SPaul B Schroeder 
8493f542974SPaul B Schroeder 	mos7840_port = mos7840_get_port_private(port);
8500de9a702SOliver Neukum 	port0 = mos7840_get_port_private(serial->port[0]);
8513f542974SPaul B Schroeder 
8520de9a702SOliver Neukum 	if (mos7840_port == NULL || port0 == NULL)
8533f542974SPaul B Schroeder 		return -ENODEV;
8543f542974SPaul B Schroeder 
8553f542974SPaul B Schroeder 	usb_clear_halt(serial->dev, port->write_urb->pipe);
8563f542974SPaul B Schroeder 	usb_clear_halt(serial->dev, port->read_urb->pipe);
8570de9a702SOliver Neukum 	port0->open_ports++;
8583f542974SPaul B Schroeder 
8593f542974SPaul B Schroeder 	/* Initialising the write urb pool */
8603f542974SPaul B Schroeder 	for (j = 0; j < NUM_URBS; ++j) {
8610de9a702SOliver Neukum 		urb = usb_alloc_urb(0, GFP_KERNEL);
8623f542974SPaul B Schroeder 		mos7840_port->write_urb_pool[j] = urb;
86310c642d0SJohan Hovold 		if (!urb)
8643f542974SPaul B Schroeder 			continue;
8653f542974SPaul B Schroeder 
866880af9dbSAlan Cox 		urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE,
867880af9dbSAlan Cox 								GFP_KERNEL);
8683f542974SPaul B Schroeder 		if (!urb->transfer_buffer) {
8690de9a702SOliver Neukum 			usb_free_urb(urb);
8700de9a702SOliver Neukum 			mos7840_port->write_urb_pool[j] = NULL;
8713f542974SPaul B Schroeder 			continue;
8723f542974SPaul B Schroeder 		}
8733f542974SPaul B Schroeder 	}
8743f542974SPaul B Schroeder 
8753f542974SPaul B Schroeder /*****************************************************************************
8763f542974SPaul B Schroeder  * Initialize MCS7840 -- Write Init values to corresponding Registers
8773f542974SPaul B Schroeder  *
8783f542974SPaul B Schroeder  * Register Index
8793f542974SPaul B Schroeder  * 1 : IER
8803f542974SPaul B Schroeder  * 2 : FCR
8813f542974SPaul B Schroeder  * 3 : LCR
8823f542974SPaul B Schroeder  * 4 : MCR
8833f542974SPaul B Schroeder  *
8843f542974SPaul B Schroeder  * 0x08 : SP1/2 Control Reg
8853f542974SPaul B Schroeder  *****************************************************************************/
8863f542974SPaul B Schroeder 
887880af9dbSAlan Cox 	/* NEED to check the following Block */
8883f542974SPaul B Schroeder 
8893f542974SPaul B Schroeder 	Data = 0x0;
8903f542974SPaul B Schroeder 	status = mos7840_get_reg_sync(port, mos7840_port->SpRegOffset, &Data);
8913f542974SPaul B Schroeder 	if (status < 0) {
8929c134a14SGreg Kroah-Hartman 		dev_dbg(&port->dev, "Reading Spreg failed\n");
8935f8a2e68SJohan Hovold 		goto err;
8943f542974SPaul B Schroeder 	}
8953f542974SPaul B Schroeder 	Data |= 0x80;
8963f542974SPaul B Schroeder 	status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data);
8973f542974SPaul B Schroeder 	if (status < 0) {
8989c134a14SGreg Kroah-Hartman 		dev_dbg(&port->dev, "writing Spreg failed\n");
8995f8a2e68SJohan Hovold 		goto err;
9003f542974SPaul B Schroeder 	}
9013f542974SPaul B Schroeder 
9023f542974SPaul B Schroeder 	Data &= ~0x80;
9033f542974SPaul B Schroeder 	status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data);
9043f542974SPaul B Schroeder 	if (status < 0) {
9059c134a14SGreg Kroah-Hartman 		dev_dbg(&port->dev, "writing Spreg failed\n");
9065f8a2e68SJohan Hovold 		goto err;
9073f542974SPaul B Schroeder 	}
908880af9dbSAlan Cox 	/* End of block to be checked */
9093f542974SPaul B Schroeder 
9103f542974SPaul B Schroeder 	Data = 0x0;
911880af9dbSAlan Cox 	status = mos7840_get_reg_sync(port, mos7840_port->ControlRegOffset,
912880af9dbSAlan Cox 									&Data);
9133f542974SPaul B Schroeder 	if (status < 0) {
9149c134a14SGreg Kroah-Hartman 		dev_dbg(&port->dev, "Reading Controlreg failed\n");
9155f8a2e68SJohan Hovold 		goto err;
9163f542974SPaul B Schroeder 	}
917880af9dbSAlan Cox 	Data |= 0x08;		/* Driver done bit */
918880af9dbSAlan Cox 	Data |= 0x20;		/* rx_disable */
919880af9dbSAlan Cox 	status = mos7840_set_reg_sync(port,
920880af9dbSAlan Cox 				mos7840_port->ControlRegOffset, Data);
9213f542974SPaul B Schroeder 	if (status < 0) {
9229c134a14SGreg Kroah-Hartman 		dev_dbg(&port->dev, "writing Controlreg failed\n");
9235f8a2e68SJohan Hovold 		goto err;
9243f542974SPaul B Schroeder 	}
925880af9dbSAlan Cox 	/* do register settings here */
926880af9dbSAlan Cox 	/* Set all regs to the device default values. */
927880af9dbSAlan Cox 	/***********************************
928880af9dbSAlan Cox 	 * First Disable all interrupts.
929880af9dbSAlan Cox 	 ***********************************/
9303f542974SPaul B Schroeder 	Data = 0x00;
9313f542974SPaul B Schroeder 	status = mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
9323f542974SPaul B Schroeder 	if (status < 0) {
9339c134a14SGreg Kroah-Hartman 		dev_dbg(&port->dev, "disabling interrupts failed\n");
9345f8a2e68SJohan Hovold 		goto err;
9353f542974SPaul B Schroeder 	}
936880af9dbSAlan Cox 	/* Set FIFO_CONTROL_REGISTER to the default value */
9373f542974SPaul B Schroeder 	Data = 0x00;
9383f542974SPaul B Schroeder 	status = mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data);
9393f542974SPaul B Schroeder 	if (status < 0) {
9409c134a14SGreg Kroah-Hartman 		dev_dbg(&port->dev, "Writing FIFO_CONTROL_REGISTER  failed\n");
9415f8a2e68SJohan Hovold 		goto err;
9423f542974SPaul B Schroeder 	}
9433f542974SPaul B Schroeder 
9443f542974SPaul B Schroeder 	Data = 0xcf;
9453f542974SPaul B Schroeder 	status = mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data);
9463f542974SPaul B Schroeder 	if (status < 0) {
9479c134a14SGreg Kroah-Hartman 		dev_dbg(&port->dev, "Writing FIFO_CONTROL_REGISTER  failed\n");
9485f8a2e68SJohan Hovold 		goto err;
9493f542974SPaul B Schroeder 	}
9503f542974SPaul B Schroeder 
9513f542974SPaul B Schroeder 	Data = 0x03;
9523f542974SPaul B Schroeder 	status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
9533f542974SPaul B Schroeder 	mos7840_port->shadowLCR = Data;
9543f542974SPaul B Schroeder 
9553f542974SPaul B Schroeder 	Data = 0x0b;
9563f542974SPaul B Schroeder 	status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
9573f542974SPaul B Schroeder 	mos7840_port->shadowMCR = Data;
9583f542974SPaul B Schroeder 
9593f542974SPaul B Schroeder 	Data = 0x00;
9603f542974SPaul B Schroeder 	status = mos7840_get_uart_reg(port, LINE_CONTROL_REGISTER, &Data);
9613f542974SPaul B Schroeder 	mos7840_port->shadowLCR = Data;
9623f542974SPaul B Schroeder 
963880af9dbSAlan Cox 	Data |= SERIAL_LCR_DLAB;	/* data latch enable in LCR 0x80 */
9643f542974SPaul B Schroeder 	status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
9653f542974SPaul B Schroeder 
9663f542974SPaul B Schroeder 	Data = 0x0c;
9673f542974SPaul B Schroeder 	status = mos7840_set_uart_reg(port, DIVISOR_LATCH_LSB, Data);
9683f542974SPaul B Schroeder 
9693f542974SPaul B Schroeder 	Data = 0x0;
9703f542974SPaul B Schroeder 	status = mos7840_set_uart_reg(port, DIVISOR_LATCH_MSB, Data);
9713f542974SPaul B Schroeder 
9723f542974SPaul B Schroeder 	Data = 0x00;
9733f542974SPaul B Schroeder 	status = mos7840_get_uart_reg(port, LINE_CONTROL_REGISTER, &Data);
9743f542974SPaul B Schroeder 
9753f542974SPaul B Schroeder 	Data = Data & ~SERIAL_LCR_DLAB;
9763f542974SPaul B Schroeder 	status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
9773f542974SPaul B Schroeder 	mos7840_port->shadowLCR = Data;
9783f542974SPaul B Schroeder 
979880af9dbSAlan Cox 	/* clearing Bulkin and Bulkout Fifo */
9803f542974SPaul B Schroeder 	Data = 0x0;
9813f542974SPaul B Schroeder 	status = mos7840_get_reg_sync(port, mos7840_port->SpRegOffset, &Data);
9823f542974SPaul B Schroeder 
9833f542974SPaul B Schroeder 	Data = Data | 0x0c;
9843f542974SPaul B Schroeder 	status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data);
9853f542974SPaul B Schroeder 
9863f542974SPaul B Schroeder 	Data = Data & ~0x0c;
9873f542974SPaul B Schroeder 	status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data);
988880af9dbSAlan Cox 	/* Finally enable all interrupts */
9893f542974SPaul B Schroeder 	Data = 0x0c;
9903f542974SPaul B Schroeder 	status = mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
9913f542974SPaul B Schroeder 
992880af9dbSAlan Cox 	/* clearing rx_disable */
9933f542974SPaul B Schroeder 	Data = 0x0;
994880af9dbSAlan Cox 	status = mos7840_get_reg_sync(port, mos7840_port->ControlRegOffset,
995880af9dbSAlan Cox 									&Data);
9963f542974SPaul B Schroeder 	Data = Data & ~0x20;
997880af9dbSAlan Cox 	status = mos7840_set_reg_sync(port, mos7840_port->ControlRegOffset,
998880af9dbSAlan Cox 									Data);
9993f542974SPaul B Schroeder 
1000880af9dbSAlan Cox 	/* rx_negate */
10013f542974SPaul B Schroeder 	Data = 0x0;
1002880af9dbSAlan Cox 	status = mos7840_get_reg_sync(port, mos7840_port->ControlRegOffset,
1003880af9dbSAlan Cox 									&Data);
10043f542974SPaul B Schroeder 	Data = Data | 0x10;
1005880af9dbSAlan Cox 	status = mos7840_set_reg_sync(port, mos7840_port->ControlRegOffset,
1006880af9dbSAlan Cox 									Data);
10073f542974SPaul B Schroeder 
10083f542974SPaul B Schroeder 	/* Check to see if we've set up our endpoint info yet    *
10093f542974SPaul B Schroeder 	 * (can't set it up in mos7840_startup as the structures *
10103f542974SPaul B Schroeder 	 * were not set up at that time.)                        */
10110de9a702SOliver Neukum 	if (port0->open_ports == 1) {
10125182c2cfSJohan Hovold 		/* FIXME: Buffer never NULL, so URB is not submitted. */
10133f542974SPaul B Schroeder 		if (serial->port[0]->interrupt_in_buffer == NULL) {
10143f542974SPaul B Schroeder 			/* set up interrupt urb */
10153f542974SPaul B Schroeder 			usb_fill_int_urb(serial->port[0]->interrupt_in_urb,
10163f542974SPaul B Schroeder 				serial->dev,
10173f542974SPaul B Schroeder 				usb_rcvintpipe(serial->dev,
1018880af9dbSAlan Cox 				serial->port[0]->interrupt_in_endpointAddress),
10193f542974SPaul B Schroeder 				serial->port[0]->interrupt_in_buffer,
10203f542974SPaul B Schroeder 				serial->port[0]->interrupt_in_urb->
10213f542974SPaul B Schroeder 				transfer_buffer_length,
10223f542974SPaul B Schroeder 				mos7840_interrupt_callback,
10233f542974SPaul B Schroeder 				serial,
1024880af9dbSAlan Cox 				serial->port[0]->interrupt_in_urb->interval);
10253f542974SPaul B Schroeder 
1026472d7e55SJohan Hovold 			/* start interrupt read for mos7840 */
10273f542974SPaul B Schroeder 			response =
10283f542974SPaul B Schroeder 			    usb_submit_urb(serial->port[0]->interrupt_in_urb,
10293f542974SPaul B Schroeder 					   GFP_KERNEL);
10303f542974SPaul B Schroeder 			if (response) {
1031194343d9SGreg Kroah-Hartman 				dev_err(&port->dev, "%s - Error %d submitting "
1032194343d9SGreg Kroah-Hartman 					"interrupt urb\n", __func__, response);
10333f542974SPaul B Schroeder 			}
10343f542974SPaul B Schroeder 
10353f542974SPaul B Schroeder 		}
10363f542974SPaul B Schroeder 
10373f542974SPaul B Schroeder 	}
10383f542974SPaul B Schroeder 
10393f542974SPaul B Schroeder 	/* see if we've set up our endpoint info yet   *
10403f542974SPaul B Schroeder 	 * (can't set it up in mos7840_startup as the  *
10413f542974SPaul B Schroeder 	 * structures were not set up at that time.)   */
10423f542974SPaul B Schroeder 
10431143832eSGreg Kroah-Hartman 	dev_dbg(&port->dev, "port number is %d\n", port->port_number);
1044e5b1e206SGreg Kroah-Hartman 	dev_dbg(&port->dev, "minor number is %d\n", port->minor);
10459c134a14SGreg Kroah-Hartman 	dev_dbg(&port->dev, "Bulkin endpoint is %d\n", port->bulk_in_endpointAddress);
10469c134a14SGreg Kroah-Hartman 	dev_dbg(&port->dev, "BulkOut endpoint is %d\n", port->bulk_out_endpointAddress);
10479c134a14SGreg Kroah-Hartman 	dev_dbg(&port->dev, "Interrupt endpoint is %d\n", port->interrupt_in_endpointAddress);
10489c134a14SGreg Kroah-Hartman 	dev_dbg(&port->dev, "port's number in the device is %d\n", mos7840_port->port_num);
10493f542974SPaul B Schroeder 	mos7840_port->read_urb = port->read_urb;
10503f542974SPaul B Schroeder 
10513f542974SPaul B Schroeder 	/* set up our bulk in urb */
10521143832eSGreg Kroah-Hartman 	if ((serial->num_ports == 2) && (((__u16)port->port_number % 2) != 0)) {
1053093ea2d3SDonald Lee 		usb_fill_bulk_urb(mos7840_port->read_urb,
1054093ea2d3SDonald Lee 			serial->dev,
1055093ea2d3SDonald Lee 			usb_rcvbulkpipe(serial->dev,
1056093ea2d3SDonald Lee 				(port->bulk_in_endpointAddress) + 2),
1057093ea2d3SDonald Lee 			port->bulk_in_buffer,
1058093ea2d3SDonald Lee 			mos7840_port->read_urb->transfer_buffer_length,
1059093ea2d3SDonald Lee 			mos7840_bulk_in_callback, mos7840_port);
1060093ea2d3SDonald Lee 	} else {
10613f542974SPaul B Schroeder 		usb_fill_bulk_urb(mos7840_port->read_urb,
10623f542974SPaul B Schroeder 			serial->dev,
10633f542974SPaul B Schroeder 			usb_rcvbulkpipe(serial->dev,
10643f542974SPaul B Schroeder 				port->bulk_in_endpointAddress),
10653f542974SPaul B Schroeder 			port->bulk_in_buffer,
10663f542974SPaul B Schroeder 			mos7840_port->read_urb->transfer_buffer_length,
10673f542974SPaul B Schroeder 			mos7840_bulk_in_callback, mos7840_port);
1068093ea2d3SDonald Lee 	}
10693f542974SPaul B Schroeder 
10709c134a14SGreg Kroah-Hartman 	dev_dbg(&port->dev, "%s: bulkin endpoint is %d\n", __func__, port->bulk_in_endpointAddress);
107150de36f7SGreg Kroah-Hartman 	mos7840_port->read_urb_busy = true;
10723f542974SPaul B Schroeder 	response = usb_submit_urb(mos7840_port->read_urb, GFP_KERNEL);
10733f542974SPaul B Schroeder 	if (response) {
1074194343d9SGreg Kroah-Hartman 		dev_err(&port->dev, "%s - Error %d submitting control urb\n",
1075194343d9SGreg Kroah-Hartman 			__func__, response);
107650de36f7SGreg Kroah-Hartman 		mos7840_port->read_urb_busy = false;
10773f542974SPaul B Schroeder 	}
10783f542974SPaul B Schroeder 
10793f542974SPaul B Schroeder 	/* initialize our port settings */
1080880af9dbSAlan Cox 	/* Must set to enable ints! */
1081880af9dbSAlan Cox 	mos7840_port->shadowMCR = MCR_MASTER_IE;
10823f542974SPaul B Schroeder 	/* send a open port command */
10833f542974SPaul B Schroeder 	mos7840_port->open = 1;
1084880af9dbSAlan Cox 	/* mos7840_change_port_settings(mos7840_port,old_termios); */
10853f542974SPaul B Schroeder 
10863f542974SPaul B Schroeder 	return 0;
10875f8a2e68SJohan Hovold err:
10885f8a2e68SJohan Hovold 	for (j = 0; j < NUM_URBS; ++j) {
10895f8a2e68SJohan Hovold 		urb = mos7840_port->write_urb_pool[j];
10905f8a2e68SJohan Hovold 		if (!urb)
10915f8a2e68SJohan Hovold 			continue;
10925f8a2e68SJohan Hovold 		kfree(urb->transfer_buffer);
10935f8a2e68SJohan Hovold 		usb_free_urb(urb);
10945f8a2e68SJohan Hovold 	}
10955f8a2e68SJohan Hovold 	return status;
10963f542974SPaul B Schroeder }
10973f542974SPaul B Schroeder 
10983f542974SPaul B Schroeder /*****************************************************************************
10993f542974SPaul B Schroeder  * mos7840_chars_in_buffer
11003f542974SPaul B Schroeder  *	this function is called by the tty driver when it wants to know how many
11013f542974SPaul B Schroeder  *	bytes of data we currently have outstanding in the port (data that has
11023f542974SPaul B Schroeder  *	been written, but hasn't made it out the port yet)
11033f542974SPaul B Schroeder  *	If successful, we return the number of bytes left to be written in the
11043f542974SPaul B Schroeder  *	system,
110595da310eSAlan Cox  *	Otherwise we return zero.
11063f542974SPaul B Schroeder  *****************************************************************************/
11073f542974SPaul B Schroeder 
110895da310eSAlan Cox static int mos7840_chars_in_buffer(struct tty_struct *tty)
11093f542974SPaul B Schroeder {
111095da310eSAlan Cox 	struct usb_serial_port *port = tty->driver_data;
11113f542974SPaul B Schroeder 	int i;
11123f542974SPaul B Schroeder 	int chars = 0;
11130de9a702SOliver Neukum 	unsigned long flags;
11143f542974SPaul B Schroeder 	struct moschip_port *mos7840_port;
11153f542974SPaul B Schroeder 
11169c134a14SGreg Kroah-Hartman 	if (mos7840_port_paranoia_check(port, __func__))
111795da310eSAlan Cox 		return 0;
11183f542974SPaul B Schroeder 
11193f542974SPaul B Schroeder 	mos7840_port = mos7840_get_port_private(port);
11203363155bSGreg Kroah-Hartman 	if (mos7840_port == NULL)
112195da310eSAlan Cox 		return 0;
11223f542974SPaul B Schroeder 
11230de9a702SOliver Neukum 	spin_lock_irqsave(&mos7840_port->pool_lock, flags);
11245c263b92SMark Ferrell 	for (i = 0; i < NUM_URBS; ++i) {
11255c263b92SMark Ferrell 		if (mos7840_port->busy[i]) {
11265c263b92SMark Ferrell 			struct urb *urb = mos7840_port->write_urb_pool[i];
11275c263b92SMark Ferrell 			chars += urb->transfer_buffer_length;
11285c263b92SMark Ferrell 		}
11295c263b92SMark Ferrell 	}
11300de9a702SOliver Neukum 	spin_unlock_irqrestore(&mos7840_port->pool_lock, flags);
11319c134a14SGreg Kroah-Hartman 	dev_dbg(&port->dev, "%s - returns %d\n", __func__, chars);
11320de9a702SOliver Neukum 	return chars;
11333f542974SPaul B Schroeder 
11343f542974SPaul B Schroeder }
11353f542974SPaul B Schroeder 
11363f542974SPaul B Schroeder /*****************************************************************************
11373f542974SPaul B Schroeder  * mos7840_close
11383f542974SPaul B Schroeder  *	this function is called by the tty driver when a port is closed
11393f542974SPaul B Schroeder  *****************************************************************************/
11403f542974SPaul B Schroeder 
1141335f8514SAlan Cox static void mos7840_close(struct usb_serial_port *port)
11423f542974SPaul B Schroeder {
11433f542974SPaul B Schroeder 	struct usb_serial *serial;
11443f542974SPaul B Schroeder 	struct moschip_port *mos7840_port;
11450de9a702SOliver Neukum 	struct moschip_port *port0;
11463f542974SPaul B Schroeder 	int j;
11473f542974SPaul B Schroeder 	__u16 Data;
11483f542974SPaul B Schroeder 
11499c134a14SGreg Kroah-Hartman 	if (mos7840_port_paranoia_check(port, __func__))
11503f542974SPaul B Schroeder 		return;
11513f542974SPaul B Schroeder 
1152441b62c1SHarvey Harrison 	serial = mos7840_get_usb_serial(port, __func__);
11539c134a14SGreg Kroah-Hartman 	if (!serial)
11543f542974SPaul B Schroeder 		return;
11553f542974SPaul B Schroeder 
11563f542974SPaul B Schroeder 	mos7840_port = mos7840_get_port_private(port);
11570de9a702SOliver Neukum 	port0 = mos7840_get_port_private(serial->port[0]);
11583f542974SPaul B Schroeder 
11590de9a702SOliver Neukum 	if (mos7840_port == NULL || port0 == NULL)
11603f542974SPaul B Schroeder 		return;
11613f542974SPaul B Schroeder 
11623f542974SPaul B Schroeder 	for (j = 0; j < NUM_URBS; ++j)
11633f542974SPaul B Schroeder 		usb_kill_urb(mos7840_port->write_urb_pool[j]);
11643f542974SPaul B Schroeder 
11653f542974SPaul B Schroeder 	/* Freeing Write URBs */
11663f542974SPaul B Schroeder 	for (j = 0; j < NUM_URBS; ++j) {
11673f542974SPaul B Schroeder 		if (mos7840_port->write_urb_pool[j]) {
116891c72df1SFabian Frederick 			kfree(mos7840_port->write_urb_pool[j]->transfer_buffer);
11693f542974SPaul B Schroeder 			usb_free_urb(mos7840_port->write_urb_pool[j]);
11703f542974SPaul B Schroeder 		}
11713f542974SPaul B Schroeder 	}
11723f542974SPaul B Schroeder 
11733f542974SPaul B Schroeder 	usb_kill_urb(mos7840_port->read_urb);
117450de36f7SGreg Kroah-Hartman 	mos7840_port->read_urb_busy = false;
1175bb3529c6SJohan Hovold 
11760de9a702SOliver Neukum 	port0->open_ports--;
11771143832eSGreg Kroah-Hartman 	dev_dbg(&port->dev, "%s in close%d\n", __func__, port0->open_ports);
11780de9a702SOliver Neukum 	if (port0->open_ports == 0) {
11793f542974SPaul B Schroeder 		if (serial->port[0]->interrupt_in_urb) {
11809c134a14SGreg Kroah-Hartman 			dev_dbg(&port->dev, "Shutdown interrupt_in_urb\n");
11810de9a702SOliver Neukum 			usb_kill_urb(serial->port[0]->interrupt_in_urb);
11823f542974SPaul B Schroeder 		}
11833f542974SPaul B Schroeder 	}
11843f542974SPaul B Schroeder 
11853f542974SPaul B Schroeder 	Data = 0x0;
11863f542974SPaul B Schroeder 	mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
11873f542974SPaul B Schroeder 
11883f542974SPaul B Schroeder 	Data = 0x00;
11893f542974SPaul B Schroeder 	mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
11903f542974SPaul B Schroeder 
11913f542974SPaul B Schroeder 	mos7840_port->open = 0;
11923f542974SPaul B Schroeder }
11933f542974SPaul B Schroeder 
11943f542974SPaul B Schroeder /*****************************************************************************
11953f542974SPaul B Schroeder  * mos7840_break
11963f542974SPaul B Schroeder  *	this function sends a break to the port
11973f542974SPaul B Schroeder  *****************************************************************************/
119895da310eSAlan Cox static void mos7840_break(struct tty_struct *tty, int break_state)
11993f542974SPaul B Schroeder {
120095da310eSAlan Cox 	struct usb_serial_port *port = tty->driver_data;
12013f542974SPaul B Schroeder 	unsigned char data;
12023f542974SPaul B Schroeder 	struct usb_serial *serial;
12033f542974SPaul B Schroeder 	struct moschip_port *mos7840_port;
12043f542974SPaul B Schroeder 
12059c134a14SGreg Kroah-Hartman 	if (mos7840_port_paranoia_check(port, __func__))
12063f542974SPaul B Schroeder 		return;
12073f542974SPaul B Schroeder 
1208441b62c1SHarvey Harrison 	serial = mos7840_get_usb_serial(port, __func__);
12099c134a14SGreg Kroah-Hartman 	if (!serial)
12103f542974SPaul B Schroeder 		return;
12113f542974SPaul B Schroeder 
12123f542974SPaul B Schroeder 	mos7840_port = mos7840_get_port_private(port);
12133f542974SPaul B Schroeder 
1214880af9dbSAlan Cox 	if (mos7840_port == NULL)
12153f542974SPaul B Schroeder 		return;
12163f542974SPaul B Schroeder 
121795da310eSAlan Cox 	if (break_state == -1)
12183f542974SPaul B Schroeder 		data = mos7840_port->shadowLCR | LCR_SET_BREAK;
121995da310eSAlan Cox 	else
12203f542974SPaul B Schroeder 		data = mos7840_port->shadowLCR & ~LCR_SET_BREAK;
12213f542974SPaul B Schroeder 
12226b447f04SAlan Cox 	/* FIXME: no locking on shadowLCR anywhere in driver */
12233f542974SPaul B Schroeder 	mos7840_port->shadowLCR = data;
12249c134a14SGreg Kroah-Hartman 	dev_dbg(&port->dev, "%s mos7840_port->shadowLCR is %x\n", __func__, mos7840_port->shadowLCR);
12253f542974SPaul B Schroeder 	mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER,
12263f542974SPaul B Schroeder 			     mos7840_port->shadowLCR);
12273f542974SPaul B Schroeder }
12283f542974SPaul B Schroeder 
12293f542974SPaul B Schroeder /*****************************************************************************
12303f542974SPaul B Schroeder  * mos7840_write_room
12313f542974SPaul B Schroeder  *	this function is called by the tty driver when it wants to know how many
12323f542974SPaul B Schroeder  *	bytes of data we can accept for a specific port.
12333f542974SPaul B Schroeder  *	If successful, we return the amount of room that we have for this port
12343f542974SPaul B Schroeder  *	Otherwise we return a negative error number.
12353f542974SPaul B Schroeder  *****************************************************************************/
12363f542974SPaul B Schroeder 
123795da310eSAlan Cox static int mos7840_write_room(struct tty_struct *tty)
12383f542974SPaul B Schroeder {
123995da310eSAlan Cox 	struct usb_serial_port *port = tty->driver_data;
12403f542974SPaul B Schroeder 	int i;
12413f542974SPaul B Schroeder 	int room = 0;
12420de9a702SOliver Neukum 	unsigned long flags;
12433f542974SPaul B Schroeder 	struct moschip_port *mos7840_port;
12443f542974SPaul B Schroeder 
12459c134a14SGreg Kroah-Hartman 	if (mos7840_port_paranoia_check(port, __func__))
12463f542974SPaul B Schroeder 		return -1;
12473f542974SPaul B Schroeder 
12483f542974SPaul B Schroeder 	mos7840_port = mos7840_get_port_private(port);
12499c134a14SGreg Kroah-Hartman 	if (mos7840_port == NULL)
12503f542974SPaul B Schroeder 		return -1;
12513f542974SPaul B Schroeder 
12520de9a702SOliver Neukum 	spin_lock_irqsave(&mos7840_port->pool_lock, flags);
12533f542974SPaul B Schroeder 	for (i = 0; i < NUM_URBS; ++i) {
1254880af9dbSAlan Cox 		if (!mos7840_port->busy[i])
12553f542974SPaul B Schroeder 			room += URB_TRANSFER_BUFFER_SIZE;
12563f542974SPaul B Schroeder 	}
12570de9a702SOliver Neukum 	spin_unlock_irqrestore(&mos7840_port->pool_lock, flags);
12583f542974SPaul B Schroeder 
12590de9a702SOliver Neukum 	room = (room == 0) ? 0 : room - URB_TRANSFER_BUFFER_SIZE + 1;
12609c134a14SGreg Kroah-Hartman 	dev_dbg(&mos7840_port->port->dev, "%s - returns %d\n", __func__, room);
12610de9a702SOliver Neukum 	return room;
12623f542974SPaul B Schroeder 
12633f542974SPaul B Schroeder }
12643f542974SPaul B Schroeder 
12653f542974SPaul B Schroeder /*****************************************************************************
12663f542974SPaul B Schroeder  * mos7840_write
12673f542974SPaul B Schroeder  *	this function is called by the tty driver when data should be written to
12683f542974SPaul B Schroeder  *	the port.
12693f542974SPaul B Schroeder  *	If successful, we return the number of bytes written, otherwise we
12703f542974SPaul B Schroeder  *      return a negative error number.
12713f542974SPaul B Schroeder  *****************************************************************************/
12723f542974SPaul B Schroeder 
127395da310eSAlan Cox static int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port,
12743f542974SPaul B Schroeder 			 const unsigned char *data, int count)
12753f542974SPaul B Schroeder {
12763f542974SPaul B Schroeder 	int status;
12773f542974SPaul B Schroeder 	int i;
12783f542974SPaul B Schroeder 	int bytes_sent = 0;
12793f542974SPaul B Schroeder 	int transfer_size;
12800de9a702SOliver Neukum 	unsigned long flags;
12813f542974SPaul B Schroeder 
12823f542974SPaul B Schroeder 	struct moschip_port *mos7840_port;
12833f542974SPaul B Schroeder 	struct usb_serial *serial;
12843f542974SPaul B Schroeder 	struct urb *urb;
1285880af9dbSAlan Cox 	/* __u16 Data; */
12863f542974SPaul B Schroeder 	const unsigned char *current_position = data;
12873f542974SPaul B Schroeder 
12889c134a14SGreg Kroah-Hartman 	if (mos7840_port_paranoia_check(port, __func__))
12893f542974SPaul B Schroeder 		return -1;
12903f542974SPaul B Schroeder 
12913f542974SPaul B Schroeder 	serial = port->serial;
12929c134a14SGreg Kroah-Hartman 	if (mos7840_serial_paranoia_check(serial, __func__))
12933f542974SPaul B Schroeder 		return -1;
12943f542974SPaul B Schroeder 
12953f542974SPaul B Schroeder 	mos7840_port = mos7840_get_port_private(port);
12969c134a14SGreg Kroah-Hartman 	if (mos7840_port == NULL)
12973f542974SPaul B Schroeder 		return -1;
12983f542974SPaul B Schroeder 
12993f542974SPaul B Schroeder 	/* try to find a free urb in the list */
13003f542974SPaul B Schroeder 	urb = NULL;
13013f542974SPaul B Schroeder 
13020de9a702SOliver Neukum 	spin_lock_irqsave(&mos7840_port->pool_lock, flags);
13033f542974SPaul B Schroeder 	for (i = 0; i < NUM_URBS; ++i) {
13040de9a702SOliver Neukum 		if (!mos7840_port->busy[i]) {
13050de9a702SOliver Neukum 			mos7840_port->busy[i] = 1;
13063f542974SPaul B Schroeder 			urb = mos7840_port->write_urb_pool[i];
13079c134a14SGreg Kroah-Hartman 			dev_dbg(&port->dev, "URB:%d\n", i);
13083f542974SPaul B Schroeder 			break;
13093f542974SPaul B Schroeder 		}
13103f542974SPaul B Schroeder 	}
13110de9a702SOliver Neukum 	spin_unlock_irqrestore(&mos7840_port->pool_lock, flags);
13123f542974SPaul B Schroeder 
13133f542974SPaul B Schroeder 	if (urb == NULL) {
13149c134a14SGreg Kroah-Hartman 		dev_dbg(&port->dev, "%s - no more free urbs\n", __func__);
13153f542974SPaul B Schroeder 		goto exit;
13163f542974SPaul B Schroeder 	}
13173f542974SPaul B Schroeder 
13183f542974SPaul B Schroeder 	if (urb->transfer_buffer == NULL) {
13193b7c7e52SAlexey Khoroshilov 		urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE,
13203b7c7e52SAlexey Khoroshilov 					       GFP_ATOMIC);
132110c642d0SJohan Hovold 		if (!urb->transfer_buffer)
13223f542974SPaul B Schroeder 			goto exit;
13233f542974SPaul B Schroeder 	}
13243f542974SPaul B Schroeder 	transfer_size = min(count, URB_TRANSFER_BUFFER_SIZE);
13253f542974SPaul B Schroeder 
13263f542974SPaul B Schroeder 	memcpy(urb->transfer_buffer, current_position, transfer_size);
13273f542974SPaul B Schroeder 
13283f542974SPaul B Schroeder 	/* fill urb with data and submit  */
13291143832eSGreg Kroah-Hartman 	if ((serial->num_ports == 2) && (((__u16)port->port_number % 2) != 0)) {
1330093ea2d3SDonald Lee 		usb_fill_bulk_urb(urb,
1331093ea2d3SDonald Lee 			serial->dev,
1332093ea2d3SDonald Lee 			usb_sndbulkpipe(serial->dev,
1333093ea2d3SDonald Lee 				(port->bulk_out_endpointAddress) + 2),
1334093ea2d3SDonald Lee 			urb->transfer_buffer,
1335093ea2d3SDonald Lee 			transfer_size,
1336093ea2d3SDonald Lee 			mos7840_bulk_out_data_callback, mos7840_port);
1337093ea2d3SDonald Lee 	} else {
13383f542974SPaul B Schroeder 		usb_fill_bulk_urb(urb,
13393f542974SPaul B Schroeder 			serial->dev,
13403f542974SPaul B Schroeder 			usb_sndbulkpipe(serial->dev,
13413f542974SPaul B Schroeder 				port->bulk_out_endpointAddress),
13423f542974SPaul B Schroeder 			urb->transfer_buffer,
13433f542974SPaul B Schroeder 			transfer_size,
13443f542974SPaul B Schroeder 			mos7840_bulk_out_data_callback, mos7840_port);
1345093ea2d3SDonald Lee 	}
13463f542974SPaul B Schroeder 
13479c134a14SGreg Kroah-Hartman 	dev_dbg(&port->dev, "bulkout endpoint is %d\n", port->bulk_out_endpointAddress);
13483f542974SPaul B Schroeder 
134905cf0decSJohan Hovold 	if (mos7840_port->has_led)
135005cf0decSJohan Hovold 		mos7840_led_activity(port);
13510eafe4deSDonald 
13523f542974SPaul B Schroeder 	/* send it down the pipe */
13533f542974SPaul B Schroeder 	status = usb_submit_urb(urb, GFP_ATOMIC);
13543f542974SPaul B Schroeder 
13553f542974SPaul B Schroeder 	if (status) {
13560de9a702SOliver Neukum 		mos7840_port->busy[i] = 0;
135722a416c4SJohan Hovold 		dev_err_console(port, "%s - usb_submit_urb(write bulk) failed "
1358194343d9SGreg Kroah-Hartman 			"with status = %d\n", __func__, status);
13593f542974SPaul B Schroeder 		bytes_sent = status;
13603f542974SPaul B Schroeder 		goto exit;
13613f542974SPaul B Schroeder 	}
13623f542974SPaul B Schroeder 	bytes_sent = transfer_size;
13638c1a07ffSJohan Hovold 	port->icount.tx += transfer_size;
13648c1a07ffSJohan Hovold 	dev_dbg(&port->dev, "icount.tx is %d:\n", port->icount.tx);
13653f542974SPaul B Schroeder exit:
13663f542974SPaul B Schroeder 	return bytes_sent;
13673f542974SPaul B Schroeder 
13683f542974SPaul B Schroeder }
13693f542974SPaul B Schroeder 
13703f542974SPaul B Schroeder /*****************************************************************************
13713f542974SPaul B Schroeder  * mos7840_throttle
13723f542974SPaul B Schroeder  *	this function is called by the tty driver when it wants to stop the data
13733f542974SPaul B Schroeder  *	being read from the port.
13743f542974SPaul B Schroeder  *****************************************************************************/
13753f542974SPaul B Schroeder 
137695da310eSAlan Cox static void mos7840_throttle(struct tty_struct *tty)
13773f542974SPaul B Schroeder {
137895da310eSAlan Cox 	struct usb_serial_port *port = tty->driver_data;
13793f542974SPaul B Schroeder 	struct moschip_port *mos7840_port;
13803f542974SPaul B Schroeder 	int status;
13813f542974SPaul B Schroeder 
13829c134a14SGreg Kroah-Hartman 	if (mos7840_port_paranoia_check(port, __func__))
13833f542974SPaul B Schroeder 		return;
13843f542974SPaul B Schroeder 
13853f542974SPaul B Schroeder 	mos7840_port = mos7840_get_port_private(port);
13863f542974SPaul B Schroeder 
13873f542974SPaul B Schroeder 	if (mos7840_port == NULL)
13883f542974SPaul B Schroeder 		return;
13893f542974SPaul B Schroeder 
13903f542974SPaul B Schroeder 	if (!mos7840_port->open) {
13919c134a14SGreg Kroah-Hartman 		dev_dbg(&port->dev, "%s", "port not opened\n");
13923f542974SPaul B Schroeder 		return;
13933f542974SPaul B Schroeder 	}
13943f542974SPaul B Schroeder 
13953f542974SPaul B Schroeder 	/* if we are implementing XON/XOFF, send the stop character */
13963f542974SPaul B Schroeder 	if (I_IXOFF(tty)) {
13973f542974SPaul B Schroeder 		unsigned char stop_char = STOP_CHAR(tty);
139895da310eSAlan Cox 		status = mos7840_write(tty, port, &stop_char, 1);
139995da310eSAlan Cox 		if (status <= 0)
14003f542974SPaul B Schroeder 			return;
14013f542974SPaul B Schroeder 	}
14023f542974SPaul B Schroeder 	/* if we are implementing RTS/CTS, toggle that line */
14039db276f8SPeter Hurley 	if (C_CRTSCTS(tty)) {
14043f542974SPaul B Schroeder 		mos7840_port->shadowMCR &= ~MCR_RTS;
140595da310eSAlan Cox 		status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER,
14063f542974SPaul B Schroeder 					 mos7840_port->shadowMCR);
140795da310eSAlan Cox 		if (status < 0)
14083f542974SPaul B Schroeder 			return;
14093f542974SPaul B Schroeder 	}
14103f542974SPaul B Schroeder }
14113f542974SPaul B Schroeder 
14123f542974SPaul B Schroeder /*****************************************************************************
14133f542974SPaul B Schroeder  * mos7840_unthrottle
1414880af9dbSAlan Cox  *	this function is called by the tty driver when it wants to resume
1415880af9dbSAlan Cox  *	the data being read from the port (called after mos7840_throttle is
1416880af9dbSAlan Cox  *	called)
14173f542974SPaul B Schroeder  *****************************************************************************/
141895da310eSAlan Cox static void mos7840_unthrottle(struct tty_struct *tty)
14193f542974SPaul B Schroeder {
142095da310eSAlan Cox 	struct usb_serial_port *port = tty->driver_data;
14213f542974SPaul B Schroeder 	int status;
14223f542974SPaul B Schroeder 	struct moschip_port *mos7840_port = mos7840_get_port_private(port);
14233f542974SPaul B Schroeder 
14249c134a14SGreg Kroah-Hartman 	if (mos7840_port_paranoia_check(port, __func__))
14253f542974SPaul B Schroeder 		return;
14263f542974SPaul B Schroeder 
14273f542974SPaul B Schroeder 	if (mos7840_port == NULL)
14283f542974SPaul B Schroeder 		return;
14293f542974SPaul B Schroeder 
14303f542974SPaul B Schroeder 	if (!mos7840_port->open) {
14319c134a14SGreg Kroah-Hartman 		dev_dbg(&port->dev, "%s - port not opened\n", __func__);
14323f542974SPaul B Schroeder 		return;
14333f542974SPaul B Schroeder 	}
14343f542974SPaul B Schroeder 
14353f542974SPaul B Schroeder 	/* if we are implementing XON/XOFF, send the start character */
14363f542974SPaul B Schroeder 	if (I_IXOFF(tty)) {
14373f542974SPaul B Schroeder 		unsigned char start_char = START_CHAR(tty);
143895da310eSAlan Cox 		status = mos7840_write(tty, port, &start_char, 1);
143995da310eSAlan Cox 		if (status <= 0)
14403f542974SPaul B Schroeder 			return;
14413f542974SPaul B Schroeder 	}
14423f542974SPaul B Schroeder 
14433f542974SPaul B Schroeder 	/* if we are implementing RTS/CTS, toggle that line */
14449db276f8SPeter Hurley 	if (C_CRTSCTS(tty)) {
14453f542974SPaul B Schroeder 		mos7840_port->shadowMCR |= MCR_RTS;
144695da310eSAlan Cox 		status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER,
14473f542974SPaul B Schroeder 					 mos7840_port->shadowMCR);
144895da310eSAlan Cox 		if (status < 0)
14493f542974SPaul B Schroeder 			return;
14503f542974SPaul B Schroeder 	}
14513f542974SPaul B Schroeder }
14523f542974SPaul B Schroeder 
145360b33c13SAlan Cox static int mos7840_tiocmget(struct tty_struct *tty)
14543f542974SPaul B Schroeder {
145595da310eSAlan Cox 	struct usb_serial_port *port = tty->driver_data;
14563f542974SPaul B Schroeder 	struct moschip_port *mos7840_port;
14573f542974SPaul B Schroeder 	unsigned int result;
14583f542974SPaul B Schroeder 	__u16 msr;
14593f542974SPaul B Schroeder 	__u16 mcr;
1460880af9dbSAlan Cox 	int status;
14613f542974SPaul B Schroeder 	mos7840_port = mos7840_get_port_private(port);
14623f542974SPaul B Schroeder 
14633f542974SPaul B Schroeder 	if (mos7840_port == NULL)
14643f542974SPaul B Schroeder 		return -ENODEV;
14653f542974SPaul B Schroeder 
14663f542974SPaul B Schroeder 	status = mos7840_get_uart_reg(port, MODEM_STATUS_REGISTER, &msr);
1467cd8db057SJohan Hovold 	if (status < 0)
1468a91ccd26SJohan Hovold 		return -EIO;
14693f542974SPaul B Schroeder 	status = mos7840_get_uart_reg(port, MODEM_CONTROL_REGISTER, &mcr);
1470cd8db057SJohan Hovold 	if (status < 0)
1471a91ccd26SJohan Hovold 		return -EIO;
14723f542974SPaul B Schroeder 	result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0)
14733f542974SPaul B Schroeder 	    | ((mcr & MCR_RTS) ? TIOCM_RTS : 0)
14743f542974SPaul B Schroeder 	    | ((mcr & MCR_LOOPBACK) ? TIOCM_LOOP : 0)
14753f542974SPaul B Schroeder 	    | ((msr & MOS7840_MSR_CTS) ? TIOCM_CTS : 0)
14763f542974SPaul B Schroeder 	    | ((msr & MOS7840_MSR_CD) ? TIOCM_CAR : 0)
14773f542974SPaul B Schroeder 	    | ((msr & MOS7840_MSR_RI) ? TIOCM_RI : 0)
14783f542974SPaul B Schroeder 	    | ((msr & MOS7840_MSR_DSR) ? TIOCM_DSR : 0);
14793f542974SPaul B Schroeder 
14809c134a14SGreg Kroah-Hartman 	dev_dbg(&port->dev, "%s - 0x%04X\n", __func__, result);
14813f542974SPaul B Schroeder 
14823f542974SPaul B Schroeder 	return result;
14833f542974SPaul B Schroeder }
14843f542974SPaul B Schroeder 
148520b9d177SAlan Cox static int mos7840_tiocmset(struct tty_struct *tty,
14863f542974SPaul B Schroeder 			    unsigned int set, unsigned int clear)
14873f542974SPaul B Schroeder {
148895da310eSAlan Cox 	struct usb_serial_port *port = tty->driver_data;
14893f542974SPaul B Schroeder 	struct moschip_port *mos7840_port;
14903f542974SPaul B Schroeder 	unsigned int mcr;
149187521c46SRoel Kluin 	int status;
14923f542974SPaul B Schroeder 
14933f542974SPaul B Schroeder 	mos7840_port = mos7840_get_port_private(port);
14943f542974SPaul B Schroeder 
14953f542974SPaul B Schroeder 	if (mos7840_port == NULL)
14963f542974SPaul B Schroeder 		return -ENODEV;
14973f542974SPaul B Schroeder 
1498e2984494SAlan Cox 	/* FIXME: What locks the port registers ? */
14993f542974SPaul B Schroeder 	mcr = mos7840_port->shadowMCR;
15003f542974SPaul B Schroeder 	if (clear & TIOCM_RTS)
15013f542974SPaul B Schroeder 		mcr &= ~MCR_RTS;
15023f542974SPaul B Schroeder 	if (clear & TIOCM_DTR)
15033f542974SPaul B Schroeder 		mcr &= ~MCR_DTR;
15043f542974SPaul B Schroeder 	if (clear & TIOCM_LOOP)
15053f542974SPaul B Schroeder 		mcr &= ~MCR_LOOPBACK;
15063f542974SPaul B Schroeder 
15073f542974SPaul B Schroeder 	if (set & TIOCM_RTS)
15083f542974SPaul B Schroeder 		mcr |= MCR_RTS;
15093f542974SPaul B Schroeder 	if (set & TIOCM_DTR)
15103f542974SPaul B Schroeder 		mcr |= MCR_DTR;
15113f542974SPaul B Schroeder 	if (set & TIOCM_LOOP)
15123f542974SPaul B Schroeder 		mcr |= MCR_LOOPBACK;
15133f542974SPaul B Schroeder 
15143f542974SPaul B Schroeder 	mos7840_port->shadowMCR = mcr;
15153f542974SPaul B Schroeder 
15163f542974SPaul B Schroeder 	status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, mcr);
15173f542974SPaul B Schroeder 	if (status < 0) {
15189c134a14SGreg Kroah-Hartman 		dev_dbg(&port->dev, "setting MODEM_CONTROL_REGISTER Failed\n");
151987521c46SRoel Kluin 		return status;
15203f542974SPaul B Schroeder 	}
15213f542974SPaul B Schroeder 
15223f542974SPaul B Schroeder 	return 0;
15233f542974SPaul B Schroeder }
15243f542974SPaul B Schroeder 
15253f542974SPaul B Schroeder /*****************************************************************************
15263f542974SPaul B Schroeder  * mos7840_calc_baud_rate_divisor
15273f542974SPaul B Schroeder  *	this function calculates the proper baud rate divisor for the specified
15283f542974SPaul B Schroeder  *	baud rate.
15293f542974SPaul B Schroeder  *****************************************************************************/
15309c134a14SGreg Kroah-Hartman static int mos7840_calc_baud_rate_divisor(struct usb_serial_port *port,
15319c134a14SGreg Kroah-Hartman 					  int baudRate, int *divisor,
15323f542974SPaul B Schroeder 					  __u16 *clk_sel_val)
15333f542974SPaul B Schroeder {
15349c134a14SGreg Kroah-Hartman 	dev_dbg(&port->dev, "%s - %d\n", __func__, baudRate);
15353f542974SPaul B Schroeder 
15363f542974SPaul B Schroeder 	if (baudRate <= 115200) {
15373f542974SPaul B Schroeder 		*divisor = 115200 / baudRate;
15383f542974SPaul B Schroeder 		*clk_sel_val = 0x0;
15393f542974SPaul B Schroeder 	}
15403f542974SPaul B Schroeder 	if ((baudRate > 115200) && (baudRate <= 230400)) {
15413f542974SPaul B Schroeder 		*divisor = 230400 / baudRate;
15423f542974SPaul B Schroeder 		*clk_sel_val = 0x10;
15433f542974SPaul B Schroeder 	} else if ((baudRate > 230400) && (baudRate <= 403200)) {
15443f542974SPaul B Schroeder 		*divisor = 403200 / baudRate;
15453f542974SPaul B Schroeder 		*clk_sel_val = 0x20;
15463f542974SPaul B Schroeder 	} else if ((baudRate > 403200) && (baudRate <= 460800)) {
15473f542974SPaul B Schroeder 		*divisor = 460800 / baudRate;
15483f542974SPaul B Schroeder 		*clk_sel_val = 0x30;
15493f542974SPaul B Schroeder 	} else if ((baudRate > 460800) && (baudRate <= 806400)) {
15503f542974SPaul B Schroeder 		*divisor = 806400 / baudRate;
15513f542974SPaul B Schroeder 		*clk_sel_val = 0x40;
15523f542974SPaul B Schroeder 	} else if ((baudRate > 806400) && (baudRate <= 921600)) {
15533f542974SPaul B Schroeder 		*divisor = 921600 / baudRate;
15543f542974SPaul B Schroeder 		*clk_sel_val = 0x50;
15553f542974SPaul B Schroeder 	} else if ((baudRate > 921600) && (baudRate <= 1572864)) {
15563f542974SPaul B Schroeder 		*divisor = 1572864 / baudRate;
15573f542974SPaul B Schroeder 		*clk_sel_val = 0x60;
15583f542974SPaul B Schroeder 	} else if ((baudRate > 1572864) && (baudRate <= 3145728)) {
15593f542974SPaul B Schroeder 		*divisor = 3145728 / baudRate;
15603f542974SPaul B Schroeder 		*clk_sel_val = 0x70;
15613f542974SPaul B Schroeder 	}
15623f542974SPaul B Schroeder 	return 0;
15633f542974SPaul B Schroeder }
15643f542974SPaul B Schroeder 
15653f542974SPaul B Schroeder /*****************************************************************************
15663f542974SPaul B Schroeder  * mos7840_send_cmd_write_baud_rate
15673f542974SPaul B Schroeder  *	this function sends the proper command to change the baud rate of the
15683f542974SPaul B Schroeder  *	specified port.
15693f542974SPaul B Schroeder  *****************************************************************************/
15703f542974SPaul B Schroeder 
15713f542974SPaul B Schroeder static int mos7840_send_cmd_write_baud_rate(struct moschip_port *mos7840_port,
15723f542974SPaul B Schroeder 					    int baudRate)
15733f542974SPaul B Schroeder {
15743f542974SPaul B Schroeder 	int divisor = 0;
15753f542974SPaul B Schroeder 	int status;
15763f542974SPaul B Schroeder 	__u16 Data;
15773f542974SPaul B Schroeder 	__u16 clk_sel_val;
15783f542974SPaul B Schroeder 	struct usb_serial_port *port;
15793f542974SPaul B Schroeder 
15803f542974SPaul B Schroeder 	if (mos7840_port == NULL)
15813f542974SPaul B Schroeder 		return -1;
15823f542974SPaul B Schroeder 
15839c134a14SGreg Kroah-Hartman 	port = mos7840_port->port;
15849c134a14SGreg Kroah-Hartman 	if (mos7840_port_paranoia_check(port, __func__))
15853f542974SPaul B Schroeder 		return -1;
15863f542974SPaul B Schroeder 
15879c134a14SGreg Kroah-Hartman 	if (mos7840_serial_paranoia_check(port->serial, __func__))
15883f542974SPaul B Schroeder 		return -1;
15893f542974SPaul B Schroeder 
15901143832eSGreg Kroah-Hartman 	dev_dbg(&port->dev, "%s - baud = %d\n", __func__, baudRate);
1591880af9dbSAlan Cox 	/* reset clk_uart_sel in spregOffset */
15923f542974SPaul B Schroeder 	if (baudRate > 115200) {
15933f542974SPaul B Schroeder #ifdef HW_flow_control
1594880af9dbSAlan Cox 		/* NOTE: need to see the pther register to modify */
1595880af9dbSAlan Cox 		/* setting h/w flow control bit to 1 */
15963f542974SPaul B Schroeder 		Data = 0x2b;
15973f542974SPaul B Schroeder 		mos7840_port->shadowMCR = Data;
1598880af9dbSAlan Cox 		status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER,
1599880af9dbSAlan Cox 									Data);
16003f542974SPaul B Schroeder 		if (status < 0) {
16019c134a14SGreg Kroah-Hartman 			dev_dbg(&port->dev, "Writing spreg failed in set_serial_baud\n");
16023f542974SPaul B Schroeder 			return -1;
16033f542974SPaul B Schroeder 		}
16043f542974SPaul B Schroeder #endif
16053f542974SPaul B Schroeder 
16063f542974SPaul B Schroeder 	} else {
16073f542974SPaul B Schroeder #ifdef HW_flow_control
1608880af9dbSAlan Cox 		/* setting h/w flow control bit to 0 */
16093f542974SPaul B Schroeder 		Data = 0xb;
16103f542974SPaul B Schroeder 		mos7840_port->shadowMCR = Data;
1611880af9dbSAlan Cox 		status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER,
1612880af9dbSAlan Cox 									Data);
16133f542974SPaul B Schroeder 		if (status < 0) {
16149c134a14SGreg Kroah-Hartman 			dev_dbg(&port->dev, "Writing spreg failed in set_serial_baud\n");
16153f542974SPaul B Schroeder 			return -1;
16163f542974SPaul B Schroeder 		}
16173f542974SPaul B Schroeder #endif
16183f542974SPaul B Schroeder 
16193f542974SPaul B Schroeder 	}
16203f542974SPaul B Schroeder 
1621880af9dbSAlan Cox 	if (1) {		/* baudRate <= 115200) */
16223f542974SPaul B Schroeder 		clk_sel_val = 0x0;
16233f542974SPaul B Schroeder 		Data = 0x0;
16249c134a14SGreg Kroah-Hartman 		status = mos7840_calc_baud_rate_divisor(port, baudRate, &divisor,
16253f542974SPaul B Schroeder 						   &clk_sel_val);
1626880af9dbSAlan Cox 		status = mos7840_get_reg_sync(port, mos7840_port->SpRegOffset,
16273f542974SPaul B Schroeder 								 &Data);
16283f542974SPaul B Schroeder 		if (status < 0) {
16299c134a14SGreg Kroah-Hartman 			dev_dbg(&port->dev, "reading spreg failed in set_serial_baud\n");
16303f542974SPaul B Schroeder 			return -1;
16313f542974SPaul B Schroeder 		}
16323f542974SPaul B Schroeder 		Data = (Data & 0x8f) | clk_sel_val;
1633880af9dbSAlan Cox 		status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset,
1634880af9dbSAlan Cox 								Data);
16353f542974SPaul B Schroeder 		if (status < 0) {
16369c134a14SGreg Kroah-Hartman 			dev_dbg(&port->dev, "Writing spreg failed in set_serial_baud\n");
16373f542974SPaul B Schroeder 			return -1;
16383f542974SPaul B Schroeder 		}
16393f542974SPaul B Schroeder 		/* Calculate the Divisor */
16403f542974SPaul B Schroeder 
16413f542974SPaul B Schroeder 		if (status) {
1642194343d9SGreg Kroah-Hartman 			dev_err(&port->dev, "%s - bad baud rate\n", __func__);
16433f542974SPaul B Schroeder 			return status;
16443f542974SPaul B Schroeder 		}
16453f542974SPaul B Schroeder 		/* Enable access to divisor latch */
16463f542974SPaul B Schroeder 		Data = mos7840_port->shadowLCR | SERIAL_LCR_DLAB;
16473f542974SPaul B Schroeder 		mos7840_port->shadowLCR = Data;
16483f542974SPaul B Schroeder 		mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
16493f542974SPaul B Schroeder 
16503f542974SPaul B Schroeder 		/* Write the divisor */
16513f542974SPaul B Schroeder 		Data = (unsigned char)(divisor & 0xff);
16529c134a14SGreg Kroah-Hartman 		dev_dbg(&port->dev, "set_serial_baud Value to write DLL is %x\n", Data);
16533f542974SPaul B Schroeder 		mos7840_set_uart_reg(port, DIVISOR_LATCH_LSB, Data);
16543f542974SPaul B Schroeder 
16553f542974SPaul B Schroeder 		Data = (unsigned char)((divisor & 0xff00) >> 8);
16569c134a14SGreg Kroah-Hartman 		dev_dbg(&port->dev, "set_serial_baud Value to write DLM is %x\n", Data);
16573f542974SPaul B Schroeder 		mos7840_set_uart_reg(port, DIVISOR_LATCH_MSB, Data);
16583f542974SPaul B Schroeder 
16593f542974SPaul B Schroeder 		/* Disable access to divisor latch */
16603f542974SPaul B Schroeder 		Data = mos7840_port->shadowLCR & ~SERIAL_LCR_DLAB;
16613f542974SPaul B Schroeder 		mos7840_port->shadowLCR = Data;
16623f542974SPaul B Schroeder 		mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
16633f542974SPaul B Schroeder 
16643f542974SPaul B Schroeder 	}
16653f542974SPaul B Schroeder 	return status;
16663f542974SPaul B Schroeder }
16673f542974SPaul B Schroeder 
16683f542974SPaul B Schroeder /*****************************************************************************
16693f542974SPaul B Schroeder  * mos7840_change_port_settings
16703f542974SPaul B Schroeder  *	This routine is called to set the UART on the device to match
16713f542974SPaul B Schroeder  *      the specified new settings.
16723f542974SPaul B Schroeder  *****************************************************************************/
16733f542974SPaul B Schroeder 
167495da310eSAlan Cox static void mos7840_change_port_settings(struct tty_struct *tty,
167595da310eSAlan Cox 	struct moschip_port *mos7840_port, struct ktermios *old_termios)
16763f542974SPaul B Schroeder {
16773f542974SPaul B Schroeder 	int baud;
16783f542974SPaul B Schroeder 	unsigned cflag;
16793f542974SPaul B Schroeder 	__u8 lData;
16803f542974SPaul B Schroeder 	__u8 lParity;
16813f542974SPaul B Schroeder 	__u8 lStop;
16823f542974SPaul B Schroeder 	int status;
16833f542974SPaul B Schroeder 	__u16 Data;
16843f542974SPaul B Schroeder 	struct usb_serial_port *port;
16853f542974SPaul B Schroeder 
16863f542974SPaul B Schroeder 	if (mos7840_port == NULL)
16873f542974SPaul B Schroeder 		return;
16883f542974SPaul B Schroeder 
16899c134a14SGreg Kroah-Hartman 	port = mos7840_port->port;
16903f542974SPaul B Schroeder 
16919c134a14SGreg Kroah-Hartman 	if (mos7840_port_paranoia_check(port, __func__))
16923f542974SPaul B Schroeder 		return;
16933f542974SPaul B Schroeder 
16949c134a14SGreg Kroah-Hartman 	if (mos7840_serial_paranoia_check(port->serial, __func__))
16953f542974SPaul B Schroeder 		return;
16963f542974SPaul B Schroeder 
16973f542974SPaul B Schroeder 	if (!mos7840_port->open) {
16989c134a14SGreg Kroah-Hartman 		dev_dbg(&port->dev, "%s - port not opened\n", __func__);
16993f542974SPaul B Schroeder 		return;
17003f542974SPaul B Schroeder 	}
17013f542974SPaul B Schroeder 
17023f542974SPaul B Schroeder 	lData = LCR_BITS_8;
17033f542974SPaul B Schroeder 	lStop = LCR_STOP_1;
17043f542974SPaul B Schroeder 	lParity = LCR_PAR_NONE;
17053f542974SPaul B Schroeder 
1706adc8d746SAlan Cox 	cflag = tty->termios.c_cflag;
17073f542974SPaul B Schroeder 
17083f542974SPaul B Schroeder 	/* Change the number of bits */
17093f542974SPaul B Schroeder 	switch (cflag & CSIZE) {
17103f542974SPaul B Schroeder 	case CS5:
17113f542974SPaul B Schroeder 		lData = LCR_BITS_5;
17123f542974SPaul B Schroeder 		break;
17133f542974SPaul B Schroeder 
17143f542974SPaul B Schroeder 	case CS6:
17153f542974SPaul B Schroeder 		lData = LCR_BITS_6;
17163f542974SPaul B Schroeder 		break;
17173f542974SPaul B Schroeder 
17183f542974SPaul B Schroeder 	case CS7:
17193f542974SPaul B Schroeder 		lData = LCR_BITS_7;
17203f542974SPaul B Schroeder 		break;
172178692cc3SColin Leitner 
17223f542974SPaul B Schroeder 	default:
17233f542974SPaul B Schroeder 	case CS8:
17243f542974SPaul B Schroeder 		lData = LCR_BITS_8;
17253f542974SPaul B Schroeder 		break;
17263f542974SPaul B Schroeder 	}
172778692cc3SColin Leitner 
17283f542974SPaul B Schroeder 	/* Change the Parity bit */
17293f542974SPaul B Schroeder 	if (cflag & PARENB) {
17303f542974SPaul B Schroeder 		if (cflag & PARODD) {
17313f542974SPaul B Schroeder 			lParity = LCR_PAR_ODD;
17329c134a14SGreg Kroah-Hartman 			dev_dbg(&port->dev, "%s - parity = odd\n", __func__);
17333f542974SPaul B Schroeder 		} else {
17343f542974SPaul B Schroeder 			lParity = LCR_PAR_EVEN;
17359c134a14SGreg Kroah-Hartman 			dev_dbg(&port->dev, "%s - parity = even\n", __func__);
17363f542974SPaul B Schroeder 		}
17373f542974SPaul B Schroeder 
17383f542974SPaul B Schroeder 	} else {
17399c134a14SGreg Kroah-Hartman 		dev_dbg(&port->dev, "%s - parity = none\n", __func__);
17403f542974SPaul B Schroeder 	}
17413f542974SPaul B Schroeder 
1742880af9dbSAlan Cox 	if (cflag & CMSPAR)
17433f542974SPaul B Schroeder 		lParity = lParity | 0x20;
17443f542974SPaul B Schroeder 
17453f542974SPaul B Schroeder 	/* Change the Stop bit */
17463f542974SPaul B Schroeder 	if (cflag & CSTOPB) {
17473f542974SPaul B Schroeder 		lStop = LCR_STOP_2;
17489c134a14SGreg Kroah-Hartman 		dev_dbg(&port->dev, "%s - stop bits = 2\n", __func__);
17493f542974SPaul B Schroeder 	} else {
17503f542974SPaul B Schroeder 		lStop = LCR_STOP_1;
17519c134a14SGreg Kroah-Hartman 		dev_dbg(&port->dev, "%s - stop bits = 1\n", __func__);
17523f542974SPaul B Schroeder 	}
17533f542974SPaul B Schroeder 
17543f542974SPaul B Schroeder 	/* Update the LCR with the correct value */
17553f542974SPaul B Schroeder 	mos7840_port->shadowLCR &=
17563f542974SPaul B Schroeder 	    ~(LCR_BITS_MASK | LCR_STOP_MASK | LCR_PAR_MASK);
17573f542974SPaul B Schroeder 	mos7840_port->shadowLCR |= (lData | lParity | lStop);
17583f542974SPaul B Schroeder 
17599c134a14SGreg Kroah-Hartman 	dev_dbg(&port->dev, "%s - mos7840_port->shadowLCR is %x\n", __func__,
17603f542974SPaul B Schroeder 		mos7840_port->shadowLCR);
17613f542974SPaul B Schroeder 	/* Disable Interrupts */
17623f542974SPaul B Schroeder 	Data = 0x00;
17633f542974SPaul B Schroeder 	mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
17643f542974SPaul B Schroeder 
17653f542974SPaul B Schroeder 	Data = 0x00;
17663f542974SPaul B Schroeder 	mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data);
17673f542974SPaul B Schroeder 
17683f542974SPaul B Schroeder 	Data = 0xcf;
17693f542974SPaul B Schroeder 	mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data);
17703f542974SPaul B Schroeder 
17713f542974SPaul B Schroeder 	/* Send the updated LCR value to the mos7840 */
17723f542974SPaul B Schroeder 	Data = mos7840_port->shadowLCR;
17733f542974SPaul B Schroeder 
17743f542974SPaul B Schroeder 	mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
17753f542974SPaul B Schroeder 
17763f542974SPaul B Schroeder 	Data = 0x00b;
17773f542974SPaul B Schroeder 	mos7840_port->shadowMCR = Data;
17783f542974SPaul B Schroeder 	mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
17793f542974SPaul B Schroeder 	Data = 0x00b;
17803f542974SPaul B Schroeder 	mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
17813f542974SPaul B Schroeder 
17823f542974SPaul B Schroeder 	/* set up the MCR register and send it to the mos7840 */
17833f542974SPaul B Schroeder 
17843f542974SPaul B Schroeder 	mos7840_port->shadowMCR = MCR_MASTER_IE;
1785880af9dbSAlan Cox 	if (cflag & CBAUD)
17863f542974SPaul B Schroeder 		mos7840_port->shadowMCR |= (MCR_DTR | MCR_RTS);
17873f542974SPaul B Schroeder 
1788880af9dbSAlan Cox 	if (cflag & CRTSCTS)
17893f542974SPaul B Schroeder 		mos7840_port->shadowMCR |= (MCR_XON_ANY);
1790880af9dbSAlan Cox 	else
17913f542974SPaul B Schroeder 		mos7840_port->shadowMCR &= ~(MCR_XON_ANY);
17923f542974SPaul B Schroeder 
17933f542974SPaul B Schroeder 	Data = mos7840_port->shadowMCR;
17943f542974SPaul B Schroeder 	mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
17953f542974SPaul B Schroeder 
17963f542974SPaul B Schroeder 	/* Determine divisor based on baud rate */
17973f542974SPaul B Schroeder 	baud = tty_get_baud_rate(tty);
17983f542974SPaul B Schroeder 
17993f542974SPaul B Schroeder 	if (!baud) {
18003f542974SPaul B Schroeder 		/* pick a default, any default... */
18019c134a14SGreg Kroah-Hartman 		dev_dbg(&port->dev, "%s", "Picked default baud...\n");
18023f542974SPaul B Schroeder 		baud = 9600;
18033f542974SPaul B Schroeder 	}
18043f542974SPaul B Schroeder 
18059c134a14SGreg Kroah-Hartman 	dev_dbg(&port->dev, "%s - baud rate = %d\n", __func__, baud);
18063f542974SPaul B Schroeder 	status = mos7840_send_cmd_write_baud_rate(mos7840_port, baud);
18073f542974SPaul B Schroeder 
18083f542974SPaul B Schroeder 	/* Enable Interrupts */
18093f542974SPaul B Schroeder 	Data = 0x0c;
18103f542974SPaul B Schroeder 	mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
18113f542974SPaul B Schroeder 
1812ce9d8562SMathieu OTHACEHE 	if (!mos7840_port->read_urb_busy) {
181350de36f7SGreg Kroah-Hartman 		mos7840_port->read_urb_busy = true;
18149d380190SJohan Hovold 		status = usb_submit_urb(mos7840_port->read_urb, GFP_KERNEL);
18153f542974SPaul B Schroeder 		if (status) {
18169c134a14SGreg Kroah-Hartman 			dev_dbg(&port->dev, "usb_submit_urb(read bulk) failed, status = %d\n",
18173f542974SPaul B Schroeder 			    status);
181850de36f7SGreg Kroah-Hartman 			mos7840_port->read_urb_busy = false;
18193f542974SPaul B Schroeder 		}
18203f542974SPaul B Schroeder 	}
18219c134a14SGreg Kroah-Hartman 	dev_dbg(&port->dev, "%s - mos7840_port->shadowLCR is End %x\n", __func__,
18223f542974SPaul B Schroeder 		mos7840_port->shadowLCR);
18233f542974SPaul B Schroeder }
18243f542974SPaul B Schroeder 
18253f542974SPaul B Schroeder /*****************************************************************************
18263f542974SPaul B Schroeder  * mos7840_set_termios
18273f542974SPaul B Schroeder  *	this function is called by the tty driver when it wants to change
18283f542974SPaul B Schroeder  *	the termios structure
18293f542974SPaul B Schroeder  *****************************************************************************/
18303f542974SPaul B Schroeder 
183195da310eSAlan Cox static void mos7840_set_termios(struct tty_struct *tty,
183295da310eSAlan Cox 				struct usb_serial_port *port,
1833606d099cSAlan Cox 				struct ktermios *old_termios)
18343f542974SPaul B Schroeder {
18353f542974SPaul B Schroeder 	int status;
18363f542974SPaul B Schroeder 	struct usb_serial *serial;
18373f542974SPaul B Schroeder 	struct moschip_port *mos7840_port;
18383363155bSGreg Kroah-Hartman 
18399c134a14SGreg Kroah-Hartman 	if (mos7840_port_paranoia_check(port, __func__))
18403f542974SPaul B Schroeder 		return;
18413f542974SPaul B Schroeder 
18423f542974SPaul B Schroeder 	serial = port->serial;
18433f542974SPaul B Schroeder 
18449c134a14SGreg Kroah-Hartman 	if (mos7840_serial_paranoia_check(serial, __func__))
18453f542974SPaul B Schroeder 		return;
18463f542974SPaul B Schroeder 
18473f542974SPaul B Schroeder 	mos7840_port = mos7840_get_port_private(port);
18483f542974SPaul B Schroeder 
18493f542974SPaul B Schroeder 	if (mos7840_port == NULL)
18503f542974SPaul B Schroeder 		return;
18513f542974SPaul B Schroeder 
18523f542974SPaul B Schroeder 	if (!mos7840_port->open) {
18539c134a14SGreg Kroah-Hartman 		dev_dbg(&port->dev, "%s - port not opened\n", __func__);
18543f542974SPaul B Schroeder 		return;
18553f542974SPaul B Schroeder 	}
18563f542974SPaul B Schroeder 
18573f542974SPaul B Schroeder 	/* change the port settings to the new ones specified */
18583f542974SPaul B Schroeder 
185995da310eSAlan Cox 	mos7840_change_port_settings(tty, mos7840_port, old_termios);
18603f542974SPaul B Schroeder 
18613f542974SPaul B Schroeder 	if (!mos7840_port->read_urb) {
18629c134a14SGreg Kroah-Hartman 		dev_dbg(&port->dev, "%s", "URB KILLED !!!!!\n");
18633f542974SPaul B Schroeder 		return;
18643f542974SPaul B Schroeder 	}
18653f542974SPaul B Schroeder 
1866ce9d8562SMathieu OTHACEHE 	if (!mos7840_port->read_urb_busy) {
186750de36f7SGreg Kroah-Hartman 		mos7840_port->read_urb_busy = true;
18689d380190SJohan Hovold 		status = usb_submit_urb(mos7840_port->read_urb, GFP_KERNEL);
18693f542974SPaul B Schroeder 		if (status) {
18709c134a14SGreg Kroah-Hartman 			dev_dbg(&port->dev, "usb_submit_urb(read bulk) failed, status = %d\n",
18713f542974SPaul B Schroeder 			    status);
187250de36f7SGreg Kroah-Hartman 			mos7840_port->read_urb_busy = false;
18733f542974SPaul B Schroeder 		}
18743f542974SPaul B Schroeder 	}
18753f542974SPaul B Schroeder }
18763f542974SPaul B Schroeder 
18773f542974SPaul B Schroeder /*****************************************************************************
18783f542974SPaul B Schroeder  * mos7840_get_lsr_info - get line status register info
18793f542974SPaul B Schroeder  *
18803f542974SPaul B Schroeder  * Purpose: Let user call ioctl() to get info when the UART physically
18813f542974SPaul B Schroeder  * 	    is emptied.  On bus types like RS485, the transmitter must
18823f542974SPaul B Schroeder  * 	    release the bus after transmitting. This must be done when
18833f542974SPaul B Schroeder  * 	    the transmit shift register is empty, not be done when the
18843f542974SPaul B Schroeder  * 	    transmit holding register is empty.  This functionality
18853f542974SPaul B Schroeder  * 	    allows an RS485 driver to be written in user space.
18863f542974SPaul B Schroeder  *****************************************************************************/
18873f542974SPaul B Schroeder 
188895da310eSAlan Cox static int mos7840_get_lsr_info(struct tty_struct *tty,
188997c4965dSAl Viro 				unsigned int __user *value)
18903f542974SPaul B Schroeder {
18913f542974SPaul B Schroeder 	int count;
18923f542974SPaul B Schroeder 	unsigned int result = 0;
18933f542974SPaul B Schroeder 
189495da310eSAlan Cox 	count = mos7840_chars_in_buffer(tty);
18959c134a14SGreg Kroah-Hartman 	if (count == 0)
18963f542974SPaul B Schroeder 		result = TIOCSER_TEMT;
18973f542974SPaul B Schroeder 
18983f542974SPaul B Schroeder 	if (copy_to_user(value, &result, sizeof(int)))
18993f542974SPaul B Schroeder 		return -EFAULT;
19003f542974SPaul B Schroeder 	return 0;
19013f542974SPaul B Schroeder }
19023f542974SPaul B Schroeder 
19033f542974SPaul B Schroeder /*****************************************************************************
19043f542974SPaul B Schroeder  * mos7840_get_serial_info
19053f542974SPaul B Schroeder  *      function to get information about serial port
19063f542974SPaul B Schroeder  *****************************************************************************/
19073f542974SPaul B Schroeder 
1908b27ef409SAl Viro static int mos7840_get_serial_info(struct tty_struct *tty,
1909b27ef409SAl Viro 				   struct serial_struct *ss)
19103f542974SPaul B Schroeder {
1911b27ef409SAl Viro 	struct usb_serial_port *port = tty->driver_data;
1912b27ef409SAl Viro 	struct moschip_port *mos7840_port = mos7840_get_port_private(port);
19133f542974SPaul B Schroeder 
1914b27ef409SAl Viro 	ss->type = PORT_16550A;
1915b27ef409SAl Viro 	ss->line = mos7840_port->port->minor;
1916b27ef409SAl Viro 	ss->port = mos7840_port->port->port_number;
1917b27ef409SAl Viro 	ss->irq = 0;
1918b27ef409SAl Viro 	ss->xmit_fifo_size = NUM_URBS * URB_TRANSFER_BUFFER_SIZE;
1919b27ef409SAl Viro 	ss->baud_base = 9600;
1920b27ef409SAl Viro 	ss->close_delay = 5 * HZ;
1921b27ef409SAl Viro 	ss->closing_wait = 30 * HZ;
19223f542974SPaul B Schroeder 	return 0;
19233f542974SPaul B Schroeder }
19243f542974SPaul B Schroeder 
19253f542974SPaul B Schroeder /*****************************************************************************
19263f542974SPaul B Schroeder  * SerialIoctl
19273f542974SPaul B Schroeder  *	this function handles any ioctl calls to the driver
19283f542974SPaul B Schroeder  *****************************************************************************/
19293f542974SPaul B Schroeder 
193000a0d0d6SAlan Cox static int mos7840_ioctl(struct tty_struct *tty,
19313f542974SPaul B Schroeder 			 unsigned int cmd, unsigned long arg)
19323f542974SPaul B Schroeder {
193395da310eSAlan Cox 	struct usb_serial_port *port = tty->driver_data;
193497c4965dSAl Viro 	void __user *argp = (void __user *)arg;
19353f542974SPaul B Schroeder 	struct moschip_port *mos7840_port;
19363f542974SPaul B Schroeder 
19379c134a14SGreg Kroah-Hartman 	if (mos7840_port_paranoia_check(port, __func__))
19383f542974SPaul B Schroeder 		return -1;
19393f542974SPaul B Schroeder 
19403f542974SPaul B Schroeder 	mos7840_port = mos7840_get_port_private(port);
19413f542974SPaul B Schroeder 
19423f542974SPaul B Schroeder 	if (mos7840_port == NULL)
19433f542974SPaul B Schroeder 		return -1;
19443f542974SPaul B Schroeder 
19453f542974SPaul B Schroeder 	switch (cmd) {
19463f542974SPaul B Schroeder 		/* return number of bytes available */
19473f542974SPaul B Schroeder 
19483f542974SPaul B Schroeder 	case TIOCSERGETLSR:
19499c134a14SGreg Kroah-Hartman 		dev_dbg(&port->dev, "%s TIOCSERGETLSR\n", __func__);
195095da310eSAlan Cox 		return mos7840_get_lsr_info(tty, argp);
19513f542974SPaul B Schroeder 
19523f542974SPaul B Schroeder 	default:
19533f542974SPaul B Schroeder 		break;
19543f542974SPaul B Schroeder 	}
19553f542974SPaul B Schroeder 	return -ENOIOCTLCMD;
19563f542974SPaul B Schroeder }
19573f542974SPaul B Schroeder 
19580eafe4deSDonald static int mos7810_check(struct usb_serial *serial)
19593f542974SPaul B Schroeder {
19600eafe4deSDonald 	int i, pass_count = 0;
196115ee89c3SJohan Hovold 	u8 *buf;
19620eafe4deSDonald 	__u16 data = 0, mcr_data = 0;
19630eafe4deSDonald 	__u16 test_pattern = 0x55AA;
196415ee89c3SJohan Hovold 	int res;
196515ee89c3SJohan Hovold 
196615ee89c3SJohan Hovold 	buf = kmalloc(VENDOR_READ_LENGTH, GFP_KERNEL);
196715ee89c3SJohan Hovold 	if (!buf)
196815ee89c3SJohan Hovold 		return 0;	/* failed to identify 7810 */
19693f542974SPaul B Schroeder 
19700eafe4deSDonald 	/* Store MCR setting */
197115ee89c3SJohan Hovold 	res = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
19720eafe4deSDonald 		MCS_RDREQ, MCS_RD_RTYPE, 0x0300, MODEM_CONTROL_REGISTER,
197315ee89c3SJohan Hovold 		buf, VENDOR_READ_LENGTH, MOS_WDR_TIMEOUT);
197415ee89c3SJohan Hovold 	if (res == VENDOR_READ_LENGTH)
197515ee89c3SJohan Hovold 		mcr_data = *buf;
19760eafe4deSDonald 
19770eafe4deSDonald 	for (i = 0; i < 16; i++) {
19780eafe4deSDonald 		/* Send the 1-bit test pattern out to MCS7810 test pin */
19790eafe4deSDonald 		usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
19800eafe4deSDonald 			MCS_WRREQ, MCS_WR_RTYPE,
19810eafe4deSDonald 			(0x0300 | (((test_pattern >> i) & 0x0001) << 1)),
19820eafe4deSDonald 			MODEM_CONTROL_REGISTER, NULL, 0, MOS_WDR_TIMEOUT);
19830eafe4deSDonald 
19840eafe4deSDonald 		/* Read the test pattern back */
198515ee89c3SJohan Hovold 		res = usb_control_msg(serial->dev,
198615ee89c3SJohan Hovold 				usb_rcvctrlpipe(serial->dev, 0), MCS_RDREQ,
198715ee89c3SJohan Hovold 				MCS_RD_RTYPE, 0, GPIO_REGISTER, buf,
1988093ea2d3SDonald Lee 				VENDOR_READ_LENGTH, MOS_WDR_TIMEOUT);
198915ee89c3SJohan Hovold 		if (res == VENDOR_READ_LENGTH)
199015ee89c3SJohan Hovold 			data = *buf;
1991093ea2d3SDonald Lee 
19920eafe4deSDonald 		/* If this is a MCS7810 device, both test patterns must match */
19930eafe4deSDonald 		if (((test_pattern >> i) ^ (~data >> 1)) & 0x0001)
19940eafe4deSDonald 			break;
19950eafe4deSDonald 
19960eafe4deSDonald 		pass_count++;
19973f542974SPaul B Schroeder 	}
1998093ea2d3SDonald Lee 
19990eafe4deSDonald 	/* Restore MCR setting */
20000eafe4deSDonald 	usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), MCS_WRREQ,
20010eafe4deSDonald 		MCS_WR_RTYPE, 0x0300 | mcr_data, MODEM_CONTROL_REGISTER, NULL,
20020eafe4deSDonald 		0, MOS_WDR_TIMEOUT);
20030eafe4deSDonald 
200415ee89c3SJohan Hovold 	kfree(buf);
200515ee89c3SJohan Hovold 
20060eafe4deSDonald 	if (pass_count == 16)
20070eafe4deSDonald 		return 1;
20080eafe4deSDonald 
20090eafe4deSDonald 	return 0;
20100eafe4deSDonald }
20110eafe4deSDonald 
201240c24f28SJohan Hovold static int mos7840_probe(struct usb_serial *serial,
201340c24f28SJohan Hovold 				const struct usb_device_id *id)
20140eafe4deSDonald {
2015*375cb533SJohan Hovold 	unsigned long device_flags = id->driver_info;
201615ee89c3SJohan Hovold 	u8 *buf;
201740c24f28SJohan Hovold 
2018*375cb533SJohan Hovold 	/* Skip device-type detection if we already have device flags. */
2019*375cb533SJohan Hovold 	if (device_flags)
202040c24f28SJohan Hovold 		goto out;
2021e696d00eSPavel Löbl 
202215ee89c3SJohan Hovold 	buf = kzalloc(VENDOR_READ_LENGTH, GFP_KERNEL);
202340c24f28SJohan Hovold 	if (!buf)
202440c24f28SJohan Hovold 		return -ENOMEM;
202540c24f28SJohan Hovold 
20260eafe4deSDonald 	usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
202715ee89c3SJohan Hovold 			MCS_RDREQ, MCS_RD_RTYPE, 0, GPIO_REGISTER, buf,
20280eafe4deSDonald 			VENDOR_READ_LENGTH, MOS_WDR_TIMEOUT);
20290eafe4deSDonald 
20300eafe4deSDonald 	/* For a MCS7840 device GPIO0 must be set to 1 */
203140c24f28SJohan Hovold 	if (buf[0] & 0x01)
2032*375cb533SJohan Hovold 		device_flags = MCS_PORTS(4);
20330eafe4deSDonald 	else if (mos7810_check(serial))
2034*375cb533SJohan Hovold 		device_flags = MCS_PORTS(1) | MCS_LED;
20350eafe4deSDonald 	else
2036*375cb533SJohan Hovold 		device_flags = MCS_PORTS(2);
203740c24f28SJohan Hovold 
203840c24f28SJohan Hovold 	kfree(buf);
203940c24f28SJohan Hovold out:
2040*375cb533SJohan Hovold 	usb_set_serial_data(serial, (void *)device_flags);
204140c24f28SJohan Hovold 
204240c24f28SJohan Hovold 	return 0;
20430eafe4deSDonald }
20440eafe4deSDonald 
204507814246SJohan Hovold static int mos7840_calc_num_ports(struct usb_serial *serial,
204607814246SJohan Hovold 					struct usb_serial_endpoints *epds)
204740c24f28SJohan Hovold {
2048*375cb533SJohan Hovold 	unsigned long device_flags = (unsigned long)usb_get_serial_data(serial);
2049*375cb533SJohan Hovold 	int num_ports = MCS_PORTS(device_flags);
205040c24f28SJohan Hovold 
2051*375cb533SJohan Hovold 	if (num_ports == 0 || num_ports > 4)
205295254020SJohan Hovold 		return -ENODEV;
20533f542974SPaul B Schroeder 
205495254020SJohan Hovold 	if (epds->num_bulk_in < num_ports || epds->num_bulk_out < num_ports) {
20555c75633eSJohan Hovold 		dev_err(&serial->interface->dev, "missing endpoints\n");
20565c75633eSJohan Hovold 		return -ENODEV;
20575c75633eSJohan Hovold 	}
20585c75633eSJohan Hovold 
205995254020SJohan Hovold 	return num_ports;
20605c75633eSJohan Hovold }
20615c75633eSJohan Hovold 
206280c00750SJohan Hovold static int mos7840_port_probe(struct usb_serial_port *port)
20633f542974SPaul B Schroeder {
206480c00750SJohan Hovold 	struct usb_serial *serial = port->serial;
2065*375cb533SJohan Hovold 	unsigned long device_flags = (unsigned long)usb_get_serial_data(serial);
20663f542974SPaul B Schroeder 	struct moschip_port *mos7840_port;
206780c00750SJohan Hovold 	int status;
206880c00750SJohan Hovold 	int pnum;
20693f542974SPaul B Schroeder 	__u16 Data;
20703f542974SPaul B Schroeder 
20713f542974SPaul B Schroeder 	/* we set up the pointers to the endpoints in the mos7840_open *
20723f542974SPaul B Schroeder 	 * function, as the structures aren't created yet.             */
20733f542974SPaul B Schroeder 
20741143832eSGreg Kroah-Hartman 	pnum = port->port_number;
207580c00750SJohan Hovold 
2076ae685effSJohan Hovold 	dev_dbg(&port->dev, "mos7840_startup: configuring port %d\n", pnum);
20777ac9da10SBurman Yan 	mos7840_port = kzalloc(sizeof(struct moschip_port), GFP_KERNEL);
207810c642d0SJohan Hovold 	if (!mos7840_port)
207980c00750SJohan Hovold 		return -ENOMEM;
20803f542974SPaul B Schroeder 
2081880af9dbSAlan Cox 	/* Initialize all port interrupt end point to port 0 int
2082880af9dbSAlan Cox 	 * endpoint. Our device has only one interrupt end point
2083880af9dbSAlan Cox 	 * common to all port */
20843f542974SPaul B Schroeder 
208580c00750SJohan Hovold 	mos7840_port->port = port;
208680c00750SJohan Hovold 	mos7840_set_port_private(port, mos7840_port);
20870de9a702SOliver Neukum 	spin_lock_init(&mos7840_port->pool_lock);
20883f542974SPaul B Schroeder 
208937768adfSTony Cook 	/* minor is not initialised until later by
209037768adfSTony Cook 	 * usb-serial.c:get_free_serial() and cannot therefore be used
209137768adfSTony Cook 	 * to index device instances */
209280c00750SJohan Hovold 	mos7840_port->port_num = pnum + 1;
2093e5b1e206SGreg Kroah-Hartman 	dev_dbg(&port->dev, "port->minor = %d\n", port->minor);
209480c00750SJohan Hovold 	dev_dbg(&port->dev, "mos7840_port->port_num = %d\n", mos7840_port->port_num);
20953f542974SPaul B Schroeder 
20963f542974SPaul B Schroeder 	if (mos7840_port->port_num == 1) {
20973f542974SPaul B Schroeder 		mos7840_port->SpRegOffset = 0x0;
20983f542974SPaul B Schroeder 		mos7840_port->ControlRegOffset = 0x1;
20993f542974SPaul B Schroeder 		mos7840_port->DcrRegOffset = 0x4;
2100e8603076SJackyChou 	} else {
2101e8603076SJackyChou 		u8 phy_num = mos7840_port->port_num;
2102e8603076SJackyChou 
2103e8603076SJackyChou 		/* Port 2 in the 2-port case uses registers of port 3 */
2104e8603076SJackyChou 		if (serial->num_ports == 2)
2105e8603076SJackyChou 			phy_num = 3;
2106e8603076SJackyChou 
2107e8603076SJackyChou 		mos7840_port->SpRegOffset = 0x8 + 2 * (phy_num - 2);
2108e8603076SJackyChou 		mos7840_port->ControlRegOffset = 0x9 + 2 * (phy_num - 2);
2109e8603076SJackyChou 		mos7840_port->DcrRegOffset = 0x16 + 3 * (phy_num - 2);
21103f542974SPaul B Schroeder 	}
211180c00750SJohan Hovold 	mos7840_dump_serial_port(port, mos7840_port);
211280c00750SJohan Hovold 	mos7840_set_port_private(port, mos7840_port);
21133f542974SPaul B Schroeder 
2114880af9dbSAlan Cox 	/* enable rx_disable bit in control register */
211580c00750SJohan Hovold 	status = mos7840_get_reg_sync(port,
21163f542974SPaul B Schroeder 			mos7840_port->ControlRegOffset, &Data);
21173f542974SPaul B Schroeder 	if (status < 0) {
211880c00750SJohan Hovold 		dev_dbg(&port->dev, "Reading ControlReg failed status-0x%x\n", status);
2119ae685effSJohan Hovold 		goto out;
21203f542974SPaul B Schroeder 	} else
212180c00750SJohan Hovold 		dev_dbg(&port->dev, "ControlReg Reading success val is %x, status%d\n", Data, status);
2122880af9dbSAlan Cox 	Data |= 0x08;	/* setting driver done bit */
2123880af9dbSAlan Cox 	Data |= 0x04;	/* sp1_bit to have cts change reflect in
2124880af9dbSAlan Cox 			   modem status reg */
21253f542974SPaul B Schroeder 
2126880af9dbSAlan Cox 	/* Data |= 0x20; //rx_disable bit */
212780c00750SJohan Hovold 	status = mos7840_set_reg_sync(port,
21283f542974SPaul B Schroeder 			mos7840_port->ControlRegOffset, Data);
21293f542974SPaul B Schroeder 	if (status < 0) {
213080c00750SJohan Hovold 		dev_dbg(&port->dev, "Writing ControlReg failed(rx_disable) status-0x%x\n", status);
2131ae685effSJohan Hovold 		goto out;
21323f542974SPaul B Schroeder 	} else
213380c00750SJohan Hovold 		dev_dbg(&port->dev, "ControlReg Writing success(rx_disable) status%d\n", status);
21343f542974SPaul B Schroeder 
2135880af9dbSAlan Cox 	/* Write default values in DCR (i.e 0x01 in DCR0, 0x05 in DCR2
2136880af9dbSAlan Cox 	   and 0x24 in DCR3 */
21373f542974SPaul B Schroeder 	Data = 0x01;
213880c00750SJohan Hovold 	status = mos7840_set_reg_sync(port,
2139880af9dbSAlan Cox 			(__u16) (mos7840_port->DcrRegOffset + 0), Data);
21403f542974SPaul B Schroeder 	if (status < 0) {
214180c00750SJohan Hovold 		dev_dbg(&port->dev, "Writing DCR0 failed status-0x%x\n", status);
2142ae685effSJohan Hovold 		goto out;
21433f542974SPaul B Schroeder 	} else
214480c00750SJohan Hovold 		dev_dbg(&port->dev, "DCR0 Writing success status%d\n", status);
21453f542974SPaul B Schroeder 
21463f542974SPaul B Schroeder 	Data = 0x05;
214780c00750SJohan Hovold 	status = mos7840_set_reg_sync(port,
2148880af9dbSAlan Cox 			(__u16) (mos7840_port->DcrRegOffset + 1), Data);
21493f542974SPaul B Schroeder 	if (status < 0) {
215080c00750SJohan Hovold 		dev_dbg(&port->dev, "Writing DCR1 failed status-0x%x\n", status);
2151ae685effSJohan Hovold 		goto out;
21523f542974SPaul B Schroeder 	} else
215380c00750SJohan Hovold 		dev_dbg(&port->dev, "DCR1 Writing success status%d\n", status);
21543f542974SPaul B Schroeder 
21553f542974SPaul B Schroeder 	Data = 0x24;
215680c00750SJohan Hovold 	status = mos7840_set_reg_sync(port,
2157880af9dbSAlan Cox 			(__u16) (mos7840_port->DcrRegOffset + 2), Data);
21583f542974SPaul B Schroeder 	if (status < 0) {
215980c00750SJohan Hovold 		dev_dbg(&port->dev, "Writing DCR2 failed status-0x%x\n", status);
2160ae685effSJohan Hovold 		goto out;
21613f542974SPaul B Schroeder 	} else
216280c00750SJohan Hovold 		dev_dbg(&port->dev, "DCR2 Writing success status%d\n", status);
21633f542974SPaul B Schroeder 
2164880af9dbSAlan Cox 	/* write values in clkstart0x0 and clkmulti 0x20 */
21653f542974SPaul B Schroeder 	Data = 0x0;
2166ae685effSJohan Hovold 	status = mos7840_set_reg_sync(port, CLK_START_VALUE_REGISTER, Data);
21673f542974SPaul B Schroeder 	if (status < 0) {
216880c00750SJohan Hovold 		dev_dbg(&port->dev, "Writing CLK_START_VALUE_REGISTER failed status-0x%x\n", status);
2169ae685effSJohan Hovold 		goto out;
21703f542974SPaul B Schroeder 	} else
217180c00750SJohan Hovold 		dev_dbg(&port->dev, "CLK_START_VALUE_REGISTER Writing success status%d\n", status);
21723f542974SPaul B Schroeder 
21733f542974SPaul B Schroeder 	Data = 0x20;
2174ae685effSJohan Hovold 	status = mos7840_set_reg_sync(port, CLK_MULTI_REGISTER, Data);
21753f542974SPaul B Schroeder 	if (status < 0) {
217680c00750SJohan Hovold 		dev_dbg(&port->dev, "Writing CLK_MULTI_REGISTER failed status-0x%x\n", status);
21770de9a702SOliver Neukum 		goto error;
21783f542974SPaul B Schroeder 	} else
217980c00750SJohan Hovold 		dev_dbg(&port->dev, "CLK_MULTI_REGISTER Writing success status%d\n", status);
21803f542974SPaul B Schroeder 
2181880af9dbSAlan Cox 	/* write value 0x0 to scratchpad register */
21823f542974SPaul B Schroeder 	Data = 0x00;
2183ae685effSJohan Hovold 	status = mos7840_set_uart_reg(port, SCRATCH_PAD_REGISTER, Data);
21843f542974SPaul B Schroeder 	if (status < 0) {
218580c00750SJohan Hovold 		dev_dbg(&port->dev, "Writing SCRATCH_PAD_REGISTER failed status-0x%x\n", status);
2186ae685effSJohan Hovold 		goto out;
21873f542974SPaul B Schroeder 	} else
218880c00750SJohan Hovold 		dev_dbg(&port->dev, "SCRATCH_PAD_REGISTER Writing success status%d\n", status);
21893f542974SPaul B Schroeder 
2190880af9dbSAlan Cox 	/* Zero Length flag register */
2191ae685effSJohan Hovold 	if ((mos7840_port->port_num != 1) && (serial->num_ports == 2)) {
21923f542974SPaul B Schroeder 		Data = 0xff;
219380c00750SJohan Hovold 		status = mos7840_set_reg_sync(port,
21943f542974SPaul B Schroeder 				(__u16) (ZLP_REG1 +
2195880af9dbSAlan Cox 					((__u16)mos7840_port->port_num)), Data);
219680c00750SJohan Hovold 		dev_dbg(&port->dev, "ZLIP offset %x\n",
21979c134a14SGreg Kroah-Hartman 				(__u16)(ZLP_REG1 + ((__u16) mos7840_port->port_num)));
21983f542974SPaul B Schroeder 		if (status < 0) {
219980c00750SJohan Hovold 			dev_dbg(&port->dev, "Writing ZLP_REG%d failed status-0x%x\n", pnum + 2, status);
2200ae685effSJohan Hovold 			goto out;
22013f542974SPaul B Schroeder 		} else
220280c00750SJohan Hovold 			dev_dbg(&port->dev, "ZLP_REG%d Writing success status%d\n", pnum + 2, status);
22033f542974SPaul B Schroeder 	} else {
22043f542974SPaul B Schroeder 		Data = 0xff;
220580c00750SJohan Hovold 		status = mos7840_set_reg_sync(port,
22063f542974SPaul B Schroeder 				(__u16) (ZLP_REG1 +
2207880af9dbSAlan Cox 					((__u16)mos7840_port->port_num) - 0x1), Data);
220880c00750SJohan Hovold 		dev_dbg(&port->dev, "ZLIP offset %x\n",
22099c134a14SGreg Kroah-Hartman 				(__u16)(ZLP_REG1 + ((__u16) mos7840_port->port_num) - 0x1));
22103f542974SPaul B Schroeder 		if (status < 0) {
221180c00750SJohan Hovold 			dev_dbg(&port->dev, "Writing ZLP_REG%d failed status-0x%x\n", pnum + 1, status);
2212ae685effSJohan Hovold 			goto out;
22133f542974SPaul B Schroeder 		} else
221480c00750SJohan Hovold 			dev_dbg(&port->dev, "ZLP_REG%d Writing success status%d\n", pnum + 1, status);
22153f542974SPaul B Schroeder 
22163f542974SPaul B Schroeder 	}
22170de9a702SOliver Neukum 	mos7840_port->control_urb = usb_alloc_urb(0, GFP_KERNEL);
22183f542974SPaul B Schroeder 	mos7840_port->ctrl_buf = kmalloc(16, GFP_KERNEL);
2219880af9dbSAlan Cox 	mos7840_port->dr = kmalloc(sizeof(struct usb_ctrlrequest),
2220880af9dbSAlan Cox 			GFP_KERNEL);
2221880af9dbSAlan Cox 	if (!mos7840_port->control_urb || !mos7840_port->ctrl_buf ||
2222880af9dbSAlan Cox 			!mos7840_port->dr) {
22230de9a702SOliver Neukum 		status = -ENOMEM;
22240de9a702SOliver Neukum 		goto error;
22250de9a702SOliver Neukum 	}
22260eafe4deSDonald 
2227*375cb533SJohan Hovold 	mos7840_port->has_led = device_flags & MCS_LED;
22280eafe4deSDonald 
22290eafe4deSDonald 	/* Initialize LED timers */
2230*375cb533SJohan Hovold 	if (mos7840_port->has_led) {
223105cf0decSJohan Hovold 		mos7840_port->led_urb = usb_alloc_urb(0, GFP_KERNEL);
223205cf0decSJohan Hovold 		mos7840_port->led_dr = kmalloc(sizeof(*mos7840_port->led_dr),
223305cf0decSJohan Hovold 								GFP_KERNEL);
223405cf0decSJohan Hovold 		if (!mos7840_port->led_urb || !mos7840_port->led_dr) {
223505cf0decSJohan Hovold 			status = -ENOMEM;
223605cf0decSJohan Hovold 			goto error;
223705cf0decSJohan Hovold 		}
223805cf0decSJohan Hovold 
2239e99e88a9SKees Cook 		timer_setup(&mos7840_port->led_timer1, mos7840_led_off, 0);
22400eafe4deSDonald 		mos7840_port->led_timer1.expires =
22410eafe4deSDonald 			jiffies + msecs_to_jiffies(LED_ON_MS);
2242e99e88a9SKees Cook 		timer_setup(&mos7840_port->led_timer2, mos7840_led_flag_off,
2243e99e88a9SKees Cook 			    0);
22440eafe4deSDonald 		mos7840_port->led_timer2.expires =
22450eafe4deSDonald 			jiffies + msecs_to_jiffies(LED_OFF_MS);
22460eafe4deSDonald 
22470eafe4deSDonald 		/* Turn off LED */
2248ae685effSJohan Hovold 		mos7840_set_led_sync(port, MODEM_CONTROL_REGISTER, 0x0300);
22490eafe4deSDonald 	}
2250ae685effSJohan Hovold out:
225180c00750SJohan Hovold 	if (pnum == serial->num_ports - 1) {
2252880af9dbSAlan Cox 		/* Zero Length flag enable */
22533f542974SPaul B Schroeder 		Data = 0x0f;
22543f542974SPaul B Schroeder 		status = mos7840_set_reg_sync(serial->port[0], ZLP_REG5, Data);
22553f542974SPaul B Schroeder 		if (status < 0) {
225680c00750SJohan Hovold 			dev_dbg(&port->dev, "Writing ZLP_REG5 failed status-0x%x\n", status);
22577ced46c3SRoel Kluin 			goto error;
22583f542974SPaul B Schroeder 		} else
225980c00750SJohan Hovold 			dev_dbg(&port->dev, "ZLP_REG5 Writing success status%d\n", status);
226080c00750SJohan Hovold 	}
22613f542974SPaul B Schroeder 	return 0;
22620de9a702SOliver Neukum error:
226305cf0decSJohan Hovold 	kfree(mos7840_port->led_dr);
226405cf0decSJohan Hovold 	usb_free_urb(mos7840_port->led_urb);
22650de9a702SOliver Neukum 	kfree(mos7840_port->dr);
22660de9a702SOliver Neukum 	kfree(mos7840_port->ctrl_buf);
22670de9a702SOliver Neukum 	usb_free_urb(mos7840_port->control_urb);
22680de9a702SOliver Neukum 	kfree(mos7840_port);
226980c00750SJohan Hovold 
22700de9a702SOliver Neukum 	return status;
22713f542974SPaul B Schroeder }
22723f542974SPaul B Schroeder 
227380c00750SJohan Hovold static int mos7840_port_remove(struct usb_serial_port *port)
22743f542974SPaul B Schroeder {
22753f542974SPaul B Schroeder 	struct moschip_port *mos7840_port;
22763f542974SPaul B Schroeder 
227780c00750SJohan Hovold 	mos7840_port = mos7840_get_port_private(port);
22783f542974SPaul B Schroeder 
22790eafe4deSDonald 	if (mos7840_port->has_led) {
22800eafe4deSDonald 		/* Turn off LED */
228180c00750SJohan Hovold 		mos7840_set_led_sync(port, MODEM_CONTROL_REGISTER, 0x0300);
22820eafe4deSDonald 
22830eafe4deSDonald 		del_timer_sync(&mos7840_port->led_timer1);
22840eafe4deSDonald 		del_timer_sync(&mos7840_port->led_timer2);
228505cf0decSJohan Hovold 
228605cf0decSJohan Hovold 		usb_kill_urb(mos7840_port->led_urb);
228705cf0decSJohan Hovold 		usb_free_urb(mos7840_port->led_urb);
228805cf0decSJohan Hovold 		kfree(mos7840_port->led_dr);
22890eafe4deSDonald 	}
229080c00750SJohan Hovold 	usb_kill_urb(mos7840_port->control_urb);
229165a4cdbbSJohan Hovold 	usb_free_urb(mos7840_port->control_urb);
22920de9a702SOliver Neukum 	kfree(mos7840_port->ctrl_buf);
22930de9a702SOliver Neukum 	kfree(mos7840_port->dr);
22943f542974SPaul B Schroeder 	kfree(mos7840_port);
229580c00750SJohan Hovold 
229680c00750SJohan Hovold 	return 0;
22973f542974SPaul B Schroeder }
22983f542974SPaul B Schroeder 
22993f542974SPaul B Schroeder static struct usb_serial_driver moschip7840_4port_device = {
23003f542974SPaul B Schroeder 	.driver = {
23013f542974SPaul B Schroeder 		   .owner = THIS_MODULE,
23023f542974SPaul B Schroeder 		   .name = "mos7840",
23033f542974SPaul B Schroeder 		   },
23043f542974SPaul B Schroeder 	.description = DRIVER_DESC,
230568e24113SGreg Kroah-Hartman 	.id_table = id_table,
230695254020SJohan Hovold 	.num_interrupt_in = 1,
23073f542974SPaul B Schroeder 	.open = mos7840_open,
23083f542974SPaul B Schroeder 	.close = mos7840_close,
23093f542974SPaul B Schroeder 	.write = mos7840_write,
23103f542974SPaul B Schroeder 	.write_room = mos7840_write_room,
23113f542974SPaul B Schroeder 	.chars_in_buffer = mos7840_chars_in_buffer,
23123f542974SPaul B Schroeder 	.throttle = mos7840_throttle,
23133f542974SPaul B Schroeder 	.unthrottle = mos7840_unthrottle,
23143f542974SPaul B Schroeder 	.calc_num_ports = mos7840_calc_num_ports,
231540c24f28SJohan Hovold 	.probe = mos7840_probe,
23163f542974SPaul B Schroeder 	.ioctl = mos7840_ioctl,
2317b27ef409SAl Viro 	.get_serial = mos7840_get_serial_info,
23183f542974SPaul B Schroeder 	.set_termios = mos7840_set_termios,
23193f542974SPaul B Schroeder 	.break_ctl = mos7840_break,
23203f542974SPaul B Schroeder 	.tiocmget = mos7840_tiocmget,
23213f542974SPaul B Schroeder 	.tiocmset = mos7840_tiocmset,
23220c613371SJohan Hovold 	.tiocmiwait = usb_serial_generic_tiocmiwait,
23238c1a07ffSJohan Hovold 	.get_icount = usb_serial_generic_get_icount,
232480c00750SJohan Hovold 	.port_probe = mos7840_port_probe,
232580c00750SJohan Hovold 	.port_remove = mos7840_port_remove,
23263f542974SPaul B Schroeder 	.read_bulk_callback = mos7840_bulk_in_callback,
23273f542974SPaul B Schroeder 	.read_int_callback = mos7840_interrupt_callback,
23283f542974SPaul B Schroeder };
23293f542974SPaul B Schroeder 
23304d2a7affSAlan Stern static struct usb_serial_driver * const serial_drivers[] = {
23314d2a7affSAlan Stern 	&moschip7840_4port_device, NULL
23324d2a7affSAlan Stern };
23334d2a7affSAlan Stern 
233468e24113SGreg Kroah-Hartman module_usb_serial_driver(serial_drivers, id_table);
23353f542974SPaul B Schroeder 
23363f542974SPaul B Schroeder MODULE_DESCRIPTION(DRIVER_DESC);
23373f542974SPaul B Schroeder MODULE_LICENSE("GPL");
2338