18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * mos7720.c
48c2ecf20Sopenharmony_ci *   Controls the Moschip 7720 usb to dual port serial converter
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright 2006 Moschip Semiconductor Tech. Ltd.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Developed by:
98c2ecf20Sopenharmony_ci * 	Vijaya Kumar <vijaykumar.gn@gmail.com>
108c2ecf20Sopenharmony_ci *	Ajay Kumar <naanuajay@yahoo.com>
118c2ecf20Sopenharmony_ci *	Gurudeva <ngurudeva@yahoo.com>
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * Cleaned up from the original by:
148c2ecf20Sopenharmony_ci *	Greg Kroah-Hartman <gregkh@suse.de>
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci * Originally based on drivers/usb/serial/io_edgeport.c which is:
178c2ecf20Sopenharmony_ci *	Copyright (C) 2000 Inside Out Networks, All rights reserved.
188c2ecf20Sopenharmony_ci *	Copyright (C) 2001-2002 Greg Kroah-Hartman <greg@kroah.com>
198c2ecf20Sopenharmony_ci */
208c2ecf20Sopenharmony_ci#include <linux/kernel.h>
218c2ecf20Sopenharmony_ci#include <linux/errno.h>
228c2ecf20Sopenharmony_ci#include <linux/slab.h>
238c2ecf20Sopenharmony_ci#include <linux/tty.h>
248c2ecf20Sopenharmony_ci#include <linux/tty_driver.h>
258c2ecf20Sopenharmony_ci#include <linux/tty_flip.h>
268c2ecf20Sopenharmony_ci#include <linux/module.h>
278c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
288c2ecf20Sopenharmony_ci#include <linux/serial.h>
298c2ecf20Sopenharmony_ci#include <linux/serial_reg.h>
308c2ecf20Sopenharmony_ci#include <linux/usb.h>
318c2ecf20Sopenharmony_ci#include <linux/usb/serial.h>
328c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
338c2ecf20Sopenharmony_ci#include <linux/parport.h>
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR "Aspire Communications pvt Ltd."
368c2ecf20Sopenharmony_ci#define DRIVER_DESC "Moschip USB Serial Driver"
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/* default urb timeout */
398c2ecf20Sopenharmony_ci#define MOS_WDR_TIMEOUT	5000
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#define MOS_MAX_PORT	0x02
428c2ecf20Sopenharmony_ci#define MOS_WRITE	0x0E
438c2ecf20Sopenharmony_ci#define MOS_READ	0x0D
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci/* Interrupt Routines Defines	*/
468c2ecf20Sopenharmony_ci#define SERIAL_IIR_RLS	0x06
478c2ecf20Sopenharmony_ci#define SERIAL_IIR_RDA	0x04
488c2ecf20Sopenharmony_ci#define SERIAL_IIR_CTI	0x0c
498c2ecf20Sopenharmony_ci#define SERIAL_IIR_THR	0x02
508c2ecf20Sopenharmony_ci#define SERIAL_IIR_MS	0x00
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci#define NUM_URBS			16	/* URB Count */
538c2ecf20Sopenharmony_ci#define URB_TRANSFER_BUFFER_SIZE	32	/* URB Size */
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci/* This structure holds all of the local serial port information */
568c2ecf20Sopenharmony_cistruct moschip_port {
578c2ecf20Sopenharmony_ci	__u8	shadowLCR;		/* last LCR value received */
588c2ecf20Sopenharmony_ci	__u8	shadowMCR;		/* last MCR value received */
598c2ecf20Sopenharmony_ci	__u8	shadowMSR;		/* last MSR value received */
608c2ecf20Sopenharmony_ci	char			open;
618c2ecf20Sopenharmony_ci	struct usb_serial_port	*port;	/* loop back to the owner */
628c2ecf20Sopenharmony_ci	struct urb		*write_urb_pool[NUM_URBS];
638c2ecf20Sopenharmony_ci};
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci#define USB_VENDOR_ID_MOSCHIP		0x9710
668c2ecf20Sopenharmony_ci#define MOSCHIP_DEVICE_ID_7720		0x7720
678c2ecf20Sopenharmony_ci#define MOSCHIP_DEVICE_ID_7715		0x7715
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic const struct usb_device_id id_table[] = {
708c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7720) },
718c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7715) },
728c2ecf20Sopenharmony_ci	{ } /* terminating entry */
738c2ecf20Sopenharmony_ci};
748c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, id_table);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci/* initial values for parport regs */
798c2ecf20Sopenharmony_ci#define DCR_INIT_VAL       0x0c	/* SLCTIN, nINIT */
808c2ecf20Sopenharmony_ci#define ECR_INIT_VAL       0x00	/* SPP mode */
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistruct urbtracker {
838c2ecf20Sopenharmony_ci	struct mos7715_parport  *mos_parport;
848c2ecf20Sopenharmony_ci	struct list_head        urblist_entry;
858c2ecf20Sopenharmony_ci	struct kref             ref_count;
868c2ecf20Sopenharmony_ci	struct urb              *urb;
878c2ecf20Sopenharmony_ci	struct usb_ctrlrequest	*setup;
888c2ecf20Sopenharmony_ci};
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cienum mos7715_pp_modes {
918c2ecf20Sopenharmony_ci	SPP = 0<<5,
928c2ecf20Sopenharmony_ci	PS2 = 1<<5,      /* moschip calls this 'NIBBLE' mode */
938c2ecf20Sopenharmony_ci	PPF = 2<<5,	 /* moschip calls this 'CB-FIFO mode */
948c2ecf20Sopenharmony_ci};
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistruct mos7715_parport {
978c2ecf20Sopenharmony_ci	struct parport          *pp;	       /* back to containing struct */
988c2ecf20Sopenharmony_ci	struct kref             ref_count;     /* to instance of this struct */
998c2ecf20Sopenharmony_ci	struct list_head        deferred_urbs; /* list deferred async urbs */
1008c2ecf20Sopenharmony_ci	struct list_head        active_urbs;   /* list async urbs in flight */
1018c2ecf20Sopenharmony_ci	spinlock_t              listlock;      /* protects list access */
1028c2ecf20Sopenharmony_ci	bool                    msg_pending;   /* usb sync call pending */
1038c2ecf20Sopenharmony_ci	struct completion       syncmsg_compl; /* usb sync call completed */
1048c2ecf20Sopenharmony_ci	struct tasklet_struct   urb_tasklet;   /* for sending deferred urbs */
1058c2ecf20Sopenharmony_ci	struct usb_serial       *serial;       /* back to containing struct */
1068c2ecf20Sopenharmony_ci	__u8	                shadowECR;     /* parallel port regs... */
1078c2ecf20Sopenharmony_ci	__u8	                shadowDCR;
1088c2ecf20Sopenharmony_ci	atomic_t                shadowDSR;     /* updated in int-in callback */
1098c2ecf20Sopenharmony_ci};
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci/* lock guards against dereferencing NULL ptr in parport ops callbacks */
1128c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(release_lock);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci#endif	/* CONFIG_USB_SERIAL_MOS7715_PARPORT */
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic const unsigned int dummy; /* for clarity in register access fns */
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cienum mos_regs {
1198c2ecf20Sopenharmony_ci	MOS7720_THR,		  /* serial port regs */
1208c2ecf20Sopenharmony_ci	MOS7720_RHR,
1218c2ecf20Sopenharmony_ci	MOS7720_IER,
1228c2ecf20Sopenharmony_ci	MOS7720_FCR,
1238c2ecf20Sopenharmony_ci	MOS7720_ISR,
1248c2ecf20Sopenharmony_ci	MOS7720_LCR,
1258c2ecf20Sopenharmony_ci	MOS7720_MCR,
1268c2ecf20Sopenharmony_ci	MOS7720_LSR,
1278c2ecf20Sopenharmony_ci	MOS7720_MSR,
1288c2ecf20Sopenharmony_ci	MOS7720_SPR,
1298c2ecf20Sopenharmony_ci	MOS7720_DLL,
1308c2ecf20Sopenharmony_ci	MOS7720_DLM,
1318c2ecf20Sopenharmony_ci	MOS7720_DPR,		  /* parallel port regs */
1328c2ecf20Sopenharmony_ci	MOS7720_DSR,
1338c2ecf20Sopenharmony_ci	MOS7720_DCR,
1348c2ecf20Sopenharmony_ci	MOS7720_ECR,
1358c2ecf20Sopenharmony_ci	MOS7720_SP1_REG,	  /* device control regs */
1368c2ecf20Sopenharmony_ci	MOS7720_SP2_REG,	  /* serial port 2 (7720 only) */
1378c2ecf20Sopenharmony_ci	MOS7720_PP_REG,
1388c2ecf20Sopenharmony_ci	MOS7720_SP_CONTROL_REG,
1398c2ecf20Sopenharmony_ci};
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci/*
1428c2ecf20Sopenharmony_ci * Return the correct value for the Windex field of the setup packet
1438c2ecf20Sopenharmony_ci * for a control endpoint message.  See the 7715 datasheet.
1448c2ecf20Sopenharmony_ci */
1458c2ecf20Sopenharmony_cistatic inline __u16 get_reg_index(enum mos_regs reg)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	static const __u16 mos7715_index_lookup_table[] = {
1488c2ecf20Sopenharmony_ci		0x00,		/* MOS7720_THR */
1498c2ecf20Sopenharmony_ci		0x00,		/* MOS7720_RHR */
1508c2ecf20Sopenharmony_ci		0x01,		/* MOS7720_IER */
1518c2ecf20Sopenharmony_ci		0x02,		/* MOS7720_FCR */
1528c2ecf20Sopenharmony_ci		0x02,		/* MOS7720_ISR */
1538c2ecf20Sopenharmony_ci		0x03,		/* MOS7720_LCR */
1548c2ecf20Sopenharmony_ci		0x04,		/* MOS7720_MCR */
1558c2ecf20Sopenharmony_ci		0x05,		/* MOS7720_LSR */
1568c2ecf20Sopenharmony_ci		0x06,		/* MOS7720_MSR */
1578c2ecf20Sopenharmony_ci		0x07,		/* MOS7720_SPR */
1588c2ecf20Sopenharmony_ci		0x00,		/* MOS7720_DLL */
1598c2ecf20Sopenharmony_ci		0x01,		/* MOS7720_DLM */
1608c2ecf20Sopenharmony_ci		0x00,		/* MOS7720_DPR */
1618c2ecf20Sopenharmony_ci		0x01,		/* MOS7720_DSR */
1628c2ecf20Sopenharmony_ci		0x02,		/* MOS7720_DCR */
1638c2ecf20Sopenharmony_ci		0x0a,		/* MOS7720_ECR */
1648c2ecf20Sopenharmony_ci		0x01,		/* MOS7720_SP1_REG */
1658c2ecf20Sopenharmony_ci		0x02,		/* MOS7720_SP2_REG (7720 only) */
1668c2ecf20Sopenharmony_ci		0x04,		/* MOS7720_PP_REG (7715 only) */
1678c2ecf20Sopenharmony_ci		0x08,		/* MOS7720_SP_CONTROL_REG */
1688c2ecf20Sopenharmony_ci	};
1698c2ecf20Sopenharmony_ci	return mos7715_index_lookup_table[reg];
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci/*
1738c2ecf20Sopenharmony_ci * Return the correct value for the upper byte of the Wvalue field of
1748c2ecf20Sopenharmony_ci * the setup packet for a control endpoint message.
1758c2ecf20Sopenharmony_ci */
1768c2ecf20Sopenharmony_cistatic inline __u16 get_reg_value(enum mos_regs reg,
1778c2ecf20Sopenharmony_ci				  unsigned int serial_portnum)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	if (reg >= MOS7720_SP1_REG)	/* control reg */
1808c2ecf20Sopenharmony_ci		return 0x0000;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	else if (reg >= MOS7720_DPR)	/* parallel port reg (7715 only) */
1838c2ecf20Sopenharmony_ci		return 0x0100;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	else			      /* serial port reg */
1868c2ecf20Sopenharmony_ci		return (serial_portnum + 2) << 8;
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci/*
1908c2ecf20Sopenharmony_ci * Write data byte to the specified device register.  The data is embedded in
1918c2ecf20Sopenharmony_ci * the value field of the setup packet. serial_portnum is ignored for registers
1928c2ecf20Sopenharmony_ci * not specific to a particular serial port.
1938c2ecf20Sopenharmony_ci */
1948c2ecf20Sopenharmony_cistatic int write_mos_reg(struct usb_serial *serial, unsigned int serial_portnum,
1958c2ecf20Sopenharmony_ci			 enum mos_regs reg, __u8 data)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	struct usb_device *usbdev = serial->dev;
1988c2ecf20Sopenharmony_ci	unsigned int pipe = usb_sndctrlpipe(usbdev, 0);
1998c2ecf20Sopenharmony_ci	__u8 request = (__u8)0x0e;
2008c2ecf20Sopenharmony_ci	__u8 requesttype = (__u8)0x40;
2018c2ecf20Sopenharmony_ci	__u16 index = get_reg_index(reg);
2028c2ecf20Sopenharmony_ci	__u16 value = get_reg_value(reg, serial_portnum) + data;
2038c2ecf20Sopenharmony_ci	int status = usb_control_msg(usbdev, pipe, request, requesttype, value,
2048c2ecf20Sopenharmony_ci				     index, NULL, 0, MOS_WDR_TIMEOUT);
2058c2ecf20Sopenharmony_ci	if (status < 0)
2068c2ecf20Sopenharmony_ci		dev_err(&usbdev->dev,
2078c2ecf20Sopenharmony_ci			"mos7720: usb_control_msg() failed: %d\n", status);
2088c2ecf20Sopenharmony_ci	return status;
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci/*
2128c2ecf20Sopenharmony_ci * Read data byte from the specified device register.  The data returned by the
2138c2ecf20Sopenharmony_ci * device is embedded in the value field of the setup packet.  serial_portnum is
2148c2ecf20Sopenharmony_ci * ignored for registers that are not specific to a particular serial port.
2158c2ecf20Sopenharmony_ci */
2168c2ecf20Sopenharmony_cistatic int read_mos_reg(struct usb_serial *serial, unsigned int serial_portnum,
2178c2ecf20Sopenharmony_ci			enum mos_regs reg, __u8 *data)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	struct usb_device *usbdev = serial->dev;
2208c2ecf20Sopenharmony_ci	unsigned int pipe = usb_rcvctrlpipe(usbdev, 0);
2218c2ecf20Sopenharmony_ci	__u8 request = (__u8)0x0d;
2228c2ecf20Sopenharmony_ci	__u8 requesttype = (__u8)0xc0;
2238c2ecf20Sopenharmony_ci	__u16 index = get_reg_index(reg);
2248c2ecf20Sopenharmony_ci	__u16 value = get_reg_value(reg, serial_portnum);
2258c2ecf20Sopenharmony_ci	u8 *buf;
2268c2ecf20Sopenharmony_ci	int status;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	buf = kmalloc(1, GFP_KERNEL);
2298c2ecf20Sopenharmony_ci	if (!buf) {
2308c2ecf20Sopenharmony_ci		*data = 0;
2318c2ecf20Sopenharmony_ci		return -ENOMEM;
2328c2ecf20Sopenharmony_ci	}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	status = usb_control_msg(usbdev, pipe, request, requesttype, value,
2358c2ecf20Sopenharmony_ci				     index, buf, 1, MOS_WDR_TIMEOUT);
2368c2ecf20Sopenharmony_ci	if (status == 1) {
2378c2ecf20Sopenharmony_ci		*data = *buf;
2388c2ecf20Sopenharmony_ci	} else {
2398c2ecf20Sopenharmony_ci		dev_err(&usbdev->dev,
2408c2ecf20Sopenharmony_ci			"mos7720: usb_control_msg() failed: %d\n", status);
2418c2ecf20Sopenharmony_ci		if (status >= 0)
2428c2ecf20Sopenharmony_ci			status = -EIO;
2438c2ecf20Sopenharmony_ci		*data = 0;
2448c2ecf20Sopenharmony_ci	}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	kfree(buf);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	return status;
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cistatic inline int mos7715_change_mode(struct mos7715_parport *mos_parport,
2548c2ecf20Sopenharmony_ci				      enum mos7715_pp_modes mode)
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	mos_parport->shadowECR = mode;
2578c2ecf20Sopenharmony_ci	write_mos_reg(mos_parport->serial, dummy, MOS7720_ECR,
2588c2ecf20Sopenharmony_ci		      mos_parport->shadowECR);
2598c2ecf20Sopenharmony_ci	return 0;
2608c2ecf20Sopenharmony_ci}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_cistatic void destroy_mos_parport(struct kref *kref)
2638c2ecf20Sopenharmony_ci{
2648c2ecf20Sopenharmony_ci	struct mos7715_parport *mos_parport =
2658c2ecf20Sopenharmony_ci		container_of(kref, struct mos7715_parport, ref_count);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	kfree(mos_parport);
2688c2ecf20Sopenharmony_ci}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_cistatic void destroy_urbtracker(struct kref *kref)
2718c2ecf20Sopenharmony_ci{
2728c2ecf20Sopenharmony_ci	struct urbtracker *urbtrack =
2738c2ecf20Sopenharmony_ci		container_of(kref, struct urbtracker, ref_count);
2748c2ecf20Sopenharmony_ci	struct mos7715_parport *mos_parport = urbtrack->mos_parport;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	usb_free_urb(urbtrack->urb);
2778c2ecf20Sopenharmony_ci	kfree(urbtrack->setup);
2788c2ecf20Sopenharmony_ci	kfree(urbtrack);
2798c2ecf20Sopenharmony_ci	kref_put(&mos_parport->ref_count, destroy_mos_parport);
2808c2ecf20Sopenharmony_ci}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci/*
2838c2ecf20Sopenharmony_ci * This runs as a tasklet when sending an urb in a non-blocking parallel
2848c2ecf20Sopenharmony_ci * port callback had to be deferred because the disconnect mutex could not be
2858c2ecf20Sopenharmony_ci * obtained at the time.
2868c2ecf20Sopenharmony_ci */
2878c2ecf20Sopenharmony_cistatic void send_deferred_urbs(struct tasklet_struct *t)
2888c2ecf20Sopenharmony_ci{
2898c2ecf20Sopenharmony_ci	int ret_val;
2908c2ecf20Sopenharmony_ci	unsigned long flags;
2918c2ecf20Sopenharmony_ci	struct mos7715_parport *mos_parport = from_tasklet(mos_parport, t,
2928c2ecf20Sopenharmony_ci							   urb_tasklet);
2938c2ecf20Sopenharmony_ci	struct urbtracker *urbtrack, *tmp;
2948c2ecf20Sopenharmony_ci	struct list_head *cursor, *next;
2958c2ecf20Sopenharmony_ci	struct device *dev;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	/* if release function ran, game over */
2988c2ecf20Sopenharmony_ci	if (unlikely(mos_parport->serial == NULL))
2998c2ecf20Sopenharmony_ci		return;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	dev = &mos_parport->serial->dev->dev;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	/* try again to get the mutex */
3048c2ecf20Sopenharmony_ci	if (!mutex_trylock(&mos_parport->serial->disc_mutex)) {
3058c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s: rescheduling tasklet\n", __func__);
3068c2ecf20Sopenharmony_ci		tasklet_schedule(&mos_parport->urb_tasklet);
3078c2ecf20Sopenharmony_ci		return;
3088c2ecf20Sopenharmony_ci	}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	/* if device disconnected, game over */
3118c2ecf20Sopenharmony_ci	if (unlikely(mos_parport->serial->disconnected)) {
3128c2ecf20Sopenharmony_ci		mutex_unlock(&mos_parport->serial->disc_mutex);
3138c2ecf20Sopenharmony_ci		return;
3148c2ecf20Sopenharmony_ci	}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	spin_lock_irqsave(&mos_parport->listlock, flags);
3178c2ecf20Sopenharmony_ci	if (list_empty(&mos_parport->deferred_urbs)) {
3188c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&mos_parport->listlock, flags);
3198c2ecf20Sopenharmony_ci		mutex_unlock(&mos_parport->serial->disc_mutex);
3208c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s: deferred_urbs list empty\n", __func__);
3218c2ecf20Sopenharmony_ci		return;
3228c2ecf20Sopenharmony_ci	}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	/* move contents of deferred_urbs list to active_urbs list and submit */
3258c2ecf20Sopenharmony_ci	list_for_each_safe(cursor, next, &mos_parport->deferred_urbs)
3268c2ecf20Sopenharmony_ci		list_move_tail(cursor, &mos_parport->active_urbs);
3278c2ecf20Sopenharmony_ci	list_for_each_entry_safe(urbtrack, tmp, &mos_parport->active_urbs,
3288c2ecf20Sopenharmony_ci			    urblist_entry) {
3298c2ecf20Sopenharmony_ci		ret_val = usb_submit_urb(urbtrack->urb, GFP_ATOMIC);
3308c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s: urb submitted\n", __func__);
3318c2ecf20Sopenharmony_ci		if (ret_val) {
3328c2ecf20Sopenharmony_ci			dev_err(dev, "usb_submit_urb() failed: %d\n", ret_val);
3338c2ecf20Sopenharmony_ci			list_del(&urbtrack->urblist_entry);
3348c2ecf20Sopenharmony_ci			kref_put(&urbtrack->ref_count, destroy_urbtracker);
3358c2ecf20Sopenharmony_ci		}
3368c2ecf20Sopenharmony_ci	}
3378c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&mos_parport->listlock, flags);
3388c2ecf20Sopenharmony_ci	mutex_unlock(&mos_parport->serial->disc_mutex);
3398c2ecf20Sopenharmony_ci}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci/* callback for parallel port control urbs submitted asynchronously */
3428c2ecf20Sopenharmony_cistatic void async_complete(struct urb *urb)
3438c2ecf20Sopenharmony_ci{
3448c2ecf20Sopenharmony_ci	struct urbtracker *urbtrack = urb->context;
3458c2ecf20Sopenharmony_ci	int status = urb->status;
3468c2ecf20Sopenharmony_ci	unsigned long flags;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	if (unlikely(status))
3498c2ecf20Sopenharmony_ci		dev_dbg(&urb->dev->dev, "%s - nonzero urb status received: %d\n", __func__, status);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	/* remove the urbtracker from the active_urbs list */
3528c2ecf20Sopenharmony_ci	spin_lock_irqsave(&urbtrack->mos_parport->listlock, flags);
3538c2ecf20Sopenharmony_ci	list_del(&urbtrack->urblist_entry);
3548c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&urbtrack->mos_parport->listlock, flags);
3558c2ecf20Sopenharmony_ci	kref_put(&urbtrack->ref_count, destroy_urbtracker);
3568c2ecf20Sopenharmony_ci}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_cistatic int write_parport_reg_nonblock(struct mos7715_parport *mos_parport,
3598c2ecf20Sopenharmony_ci				      enum mos_regs reg, __u8 data)
3608c2ecf20Sopenharmony_ci{
3618c2ecf20Sopenharmony_ci	struct urbtracker *urbtrack;
3628c2ecf20Sopenharmony_ci	int ret_val;
3638c2ecf20Sopenharmony_ci	unsigned long flags;
3648c2ecf20Sopenharmony_ci	struct usb_serial *serial = mos_parport->serial;
3658c2ecf20Sopenharmony_ci	struct usb_device *usbdev = serial->dev;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	/* create and initialize the control urb and containing urbtracker */
3688c2ecf20Sopenharmony_ci	urbtrack = kmalloc(sizeof(struct urbtracker), GFP_ATOMIC);
3698c2ecf20Sopenharmony_ci	if (!urbtrack)
3708c2ecf20Sopenharmony_ci		return -ENOMEM;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	urbtrack->urb = usb_alloc_urb(0, GFP_ATOMIC);
3738c2ecf20Sopenharmony_ci	if (!urbtrack->urb) {
3748c2ecf20Sopenharmony_ci		kfree(urbtrack);
3758c2ecf20Sopenharmony_ci		return -ENOMEM;
3768c2ecf20Sopenharmony_ci	}
3778c2ecf20Sopenharmony_ci	urbtrack->setup = kmalloc(sizeof(*urbtrack->setup), GFP_ATOMIC);
3788c2ecf20Sopenharmony_ci	if (!urbtrack->setup) {
3798c2ecf20Sopenharmony_ci		usb_free_urb(urbtrack->urb);
3808c2ecf20Sopenharmony_ci		kfree(urbtrack);
3818c2ecf20Sopenharmony_ci		return -ENOMEM;
3828c2ecf20Sopenharmony_ci	}
3838c2ecf20Sopenharmony_ci	urbtrack->setup->bRequestType = (__u8)0x40;
3848c2ecf20Sopenharmony_ci	urbtrack->setup->bRequest = (__u8)0x0e;
3858c2ecf20Sopenharmony_ci	urbtrack->setup->wValue = cpu_to_le16(get_reg_value(reg, dummy));
3868c2ecf20Sopenharmony_ci	urbtrack->setup->wIndex = cpu_to_le16(get_reg_index(reg));
3878c2ecf20Sopenharmony_ci	urbtrack->setup->wLength = 0;
3888c2ecf20Sopenharmony_ci	usb_fill_control_urb(urbtrack->urb, usbdev,
3898c2ecf20Sopenharmony_ci			     usb_sndctrlpipe(usbdev, 0),
3908c2ecf20Sopenharmony_ci			     (unsigned char *)urbtrack->setup,
3918c2ecf20Sopenharmony_ci			     NULL, 0, async_complete, urbtrack);
3928c2ecf20Sopenharmony_ci	kref_get(&mos_parport->ref_count);
3938c2ecf20Sopenharmony_ci	urbtrack->mos_parport = mos_parport;
3948c2ecf20Sopenharmony_ci	kref_init(&urbtrack->ref_count);
3958c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&urbtrack->urblist_entry);
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	/*
3988c2ecf20Sopenharmony_ci	 * get the disconnect mutex, or add tracker to the deferred_urbs list
3998c2ecf20Sopenharmony_ci	 * and schedule a tasklet to try again later
4008c2ecf20Sopenharmony_ci	 */
4018c2ecf20Sopenharmony_ci	if (!mutex_trylock(&serial->disc_mutex)) {
4028c2ecf20Sopenharmony_ci		spin_lock_irqsave(&mos_parport->listlock, flags);
4038c2ecf20Sopenharmony_ci		list_add_tail(&urbtrack->urblist_entry,
4048c2ecf20Sopenharmony_ci			      &mos_parport->deferred_urbs);
4058c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&mos_parport->listlock, flags);
4068c2ecf20Sopenharmony_ci		tasklet_schedule(&mos_parport->urb_tasklet);
4078c2ecf20Sopenharmony_ci		dev_dbg(&usbdev->dev, "tasklet scheduled\n");
4088c2ecf20Sopenharmony_ci		return 0;
4098c2ecf20Sopenharmony_ci	}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	/* bail if device disconnected */
4128c2ecf20Sopenharmony_ci	if (serial->disconnected) {
4138c2ecf20Sopenharmony_ci		kref_put(&urbtrack->ref_count, destroy_urbtracker);
4148c2ecf20Sopenharmony_ci		mutex_unlock(&serial->disc_mutex);
4158c2ecf20Sopenharmony_ci		return -ENODEV;
4168c2ecf20Sopenharmony_ci	}
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	/* add the tracker to the active_urbs list and submit */
4198c2ecf20Sopenharmony_ci	spin_lock_irqsave(&mos_parport->listlock, flags);
4208c2ecf20Sopenharmony_ci	list_add_tail(&urbtrack->urblist_entry, &mos_parport->active_urbs);
4218c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&mos_parport->listlock, flags);
4228c2ecf20Sopenharmony_ci	ret_val = usb_submit_urb(urbtrack->urb, GFP_ATOMIC);
4238c2ecf20Sopenharmony_ci	mutex_unlock(&serial->disc_mutex);
4248c2ecf20Sopenharmony_ci	if (ret_val) {
4258c2ecf20Sopenharmony_ci		dev_err(&usbdev->dev,
4268c2ecf20Sopenharmony_ci			"%s: submit_urb() failed: %d\n", __func__, ret_val);
4278c2ecf20Sopenharmony_ci		spin_lock_irqsave(&mos_parport->listlock, flags);
4288c2ecf20Sopenharmony_ci		list_del(&urbtrack->urblist_entry);
4298c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&mos_parport->listlock, flags);
4308c2ecf20Sopenharmony_ci		kref_put(&urbtrack->ref_count, destroy_urbtracker);
4318c2ecf20Sopenharmony_ci		return ret_val;
4328c2ecf20Sopenharmony_ci	}
4338c2ecf20Sopenharmony_ci	return 0;
4348c2ecf20Sopenharmony_ci}
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci/*
4378c2ecf20Sopenharmony_ci * This is the the common top part of all parallel port callback operations that
4388c2ecf20Sopenharmony_ci * send synchronous messages to the device.  This implements convoluted locking
4398c2ecf20Sopenharmony_ci * that avoids two scenarios: (1) a port operation is called after usbserial
4408c2ecf20Sopenharmony_ci * has called our release function, at which point struct mos7715_parport has
4418c2ecf20Sopenharmony_ci * been destroyed, and (2) the device has been disconnected, but usbserial has
4428c2ecf20Sopenharmony_ci * not called the release function yet because someone has a serial port open.
4438c2ecf20Sopenharmony_ci * The shared release_lock prevents the first, and the mutex and disconnected
4448c2ecf20Sopenharmony_ci * flag maintained by usbserial covers the second.  We also use the msg_pending
4458c2ecf20Sopenharmony_ci * flag to ensure that all synchronous usb message calls have completed before
4468c2ecf20Sopenharmony_ci * our release function can return.
4478c2ecf20Sopenharmony_ci */
4488c2ecf20Sopenharmony_cistatic int parport_prologue(struct parport *pp)
4498c2ecf20Sopenharmony_ci{
4508c2ecf20Sopenharmony_ci	struct mos7715_parport *mos_parport;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	spin_lock(&release_lock);
4538c2ecf20Sopenharmony_ci	mos_parport = pp->private_data;
4548c2ecf20Sopenharmony_ci	if (unlikely(mos_parport == NULL)) {
4558c2ecf20Sopenharmony_ci		/* release fn called, port struct destroyed */
4568c2ecf20Sopenharmony_ci		spin_unlock(&release_lock);
4578c2ecf20Sopenharmony_ci		return -1;
4588c2ecf20Sopenharmony_ci	}
4598c2ecf20Sopenharmony_ci	mos_parport->msg_pending = true;   /* synch usb call pending */
4608c2ecf20Sopenharmony_ci	reinit_completion(&mos_parport->syncmsg_compl);
4618c2ecf20Sopenharmony_ci	spin_unlock(&release_lock);
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	mutex_lock(&mos_parport->serial->disc_mutex);
4648c2ecf20Sopenharmony_ci	if (mos_parport->serial->disconnected) {
4658c2ecf20Sopenharmony_ci		/* device disconnected */
4668c2ecf20Sopenharmony_ci		mutex_unlock(&mos_parport->serial->disc_mutex);
4678c2ecf20Sopenharmony_ci		mos_parport->msg_pending = false;
4688c2ecf20Sopenharmony_ci		complete(&mos_parport->syncmsg_compl);
4698c2ecf20Sopenharmony_ci		return -1;
4708c2ecf20Sopenharmony_ci	}
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	return 0;
4738c2ecf20Sopenharmony_ci}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci/*
4768c2ecf20Sopenharmony_ci * This is the common bottom part of all parallel port functions that send
4778c2ecf20Sopenharmony_ci * synchronous messages to the device.
4788c2ecf20Sopenharmony_ci */
4798c2ecf20Sopenharmony_cistatic inline void parport_epilogue(struct parport *pp)
4808c2ecf20Sopenharmony_ci{
4818c2ecf20Sopenharmony_ci	struct mos7715_parport *mos_parport = pp->private_data;
4828c2ecf20Sopenharmony_ci	mutex_unlock(&mos_parport->serial->disc_mutex);
4838c2ecf20Sopenharmony_ci	mos_parport->msg_pending = false;
4848c2ecf20Sopenharmony_ci	complete(&mos_parport->syncmsg_compl);
4858c2ecf20Sopenharmony_ci}
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_cistatic void parport_mos7715_write_data(struct parport *pp, unsigned char d)
4888c2ecf20Sopenharmony_ci{
4898c2ecf20Sopenharmony_ci	struct mos7715_parport *mos_parport = pp->private_data;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	if (parport_prologue(pp) < 0)
4928c2ecf20Sopenharmony_ci		return;
4938c2ecf20Sopenharmony_ci	mos7715_change_mode(mos_parport, SPP);
4948c2ecf20Sopenharmony_ci	write_mos_reg(mos_parport->serial, dummy, MOS7720_DPR, (__u8)d);
4958c2ecf20Sopenharmony_ci	parport_epilogue(pp);
4968c2ecf20Sopenharmony_ci}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_cistatic unsigned char parport_mos7715_read_data(struct parport *pp)
4998c2ecf20Sopenharmony_ci{
5008c2ecf20Sopenharmony_ci	struct mos7715_parport *mos_parport = pp->private_data;
5018c2ecf20Sopenharmony_ci	unsigned char d;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	if (parport_prologue(pp) < 0)
5048c2ecf20Sopenharmony_ci		return 0;
5058c2ecf20Sopenharmony_ci	read_mos_reg(mos_parport->serial, dummy, MOS7720_DPR, &d);
5068c2ecf20Sopenharmony_ci	parport_epilogue(pp);
5078c2ecf20Sopenharmony_ci	return d;
5088c2ecf20Sopenharmony_ci}
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_cistatic void parport_mos7715_write_control(struct parport *pp, unsigned char d)
5118c2ecf20Sopenharmony_ci{
5128c2ecf20Sopenharmony_ci	struct mos7715_parport *mos_parport = pp->private_data;
5138c2ecf20Sopenharmony_ci	__u8 data;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	if (parport_prologue(pp) < 0)
5168c2ecf20Sopenharmony_ci		return;
5178c2ecf20Sopenharmony_ci	data = ((__u8)d & 0x0f) | (mos_parport->shadowDCR & 0xf0);
5188c2ecf20Sopenharmony_ci	write_mos_reg(mos_parport->serial, dummy, MOS7720_DCR, data);
5198c2ecf20Sopenharmony_ci	mos_parport->shadowDCR = data;
5208c2ecf20Sopenharmony_ci	parport_epilogue(pp);
5218c2ecf20Sopenharmony_ci}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_cistatic unsigned char parport_mos7715_read_control(struct parport *pp)
5248c2ecf20Sopenharmony_ci{
5258c2ecf20Sopenharmony_ci	struct mos7715_parport *mos_parport;
5268c2ecf20Sopenharmony_ci	__u8 dcr;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	spin_lock(&release_lock);
5298c2ecf20Sopenharmony_ci	mos_parport = pp->private_data;
5308c2ecf20Sopenharmony_ci	if (unlikely(mos_parport == NULL)) {
5318c2ecf20Sopenharmony_ci		spin_unlock(&release_lock);
5328c2ecf20Sopenharmony_ci		return 0;
5338c2ecf20Sopenharmony_ci	}
5348c2ecf20Sopenharmony_ci	dcr = mos_parport->shadowDCR & 0x0f;
5358c2ecf20Sopenharmony_ci	spin_unlock(&release_lock);
5368c2ecf20Sopenharmony_ci	return dcr;
5378c2ecf20Sopenharmony_ci}
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_cistatic unsigned char parport_mos7715_frob_control(struct parport *pp,
5408c2ecf20Sopenharmony_ci						  unsigned char mask,
5418c2ecf20Sopenharmony_ci						  unsigned char val)
5428c2ecf20Sopenharmony_ci{
5438c2ecf20Sopenharmony_ci	struct mos7715_parport *mos_parport = pp->private_data;
5448c2ecf20Sopenharmony_ci	__u8 dcr;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	mask &= 0x0f;
5478c2ecf20Sopenharmony_ci	val &= 0x0f;
5488c2ecf20Sopenharmony_ci	if (parport_prologue(pp) < 0)
5498c2ecf20Sopenharmony_ci		return 0;
5508c2ecf20Sopenharmony_ci	mos_parport->shadowDCR = (mos_parport->shadowDCR & (~mask)) ^ val;
5518c2ecf20Sopenharmony_ci	write_mos_reg(mos_parport->serial, dummy, MOS7720_DCR,
5528c2ecf20Sopenharmony_ci		      mos_parport->shadowDCR);
5538c2ecf20Sopenharmony_ci	dcr = mos_parport->shadowDCR & 0x0f;
5548c2ecf20Sopenharmony_ci	parport_epilogue(pp);
5558c2ecf20Sopenharmony_ci	return dcr;
5568c2ecf20Sopenharmony_ci}
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_cistatic unsigned char parport_mos7715_read_status(struct parport *pp)
5598c2ecf20Sopenharmony_ci{
5608c2ecf20Sopenharmony_ci	unsigned char status;
5618c2ecf20Sopenharmony_ci	struct mos7715_parport *mos_parport;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	spin_lock(&release_lock);
5648c2ecf20Sopenharmony_ci	mos_parport = pp->private_data;
5658c2ecf20Sopenharmony_ci	if (unlikely(mos_parport == NULL)) {	/* release called */
5668c2ecf20Sopenharmony_ci		spin_unlock(&release_lock);
5678c2ecf20Sopenharmony_ci		return 0;
5688c2ecf20Sopenharmony_ci	}
5698c2ecf20Sopenharmony_ci	status = atomic_read(&mos_parport->shadowDSR) & 0xf8;
5708c2ecf20Sopenharmony_ci	spin_unlock(&release_lock);
5718c2ecf20Sopenharmony_ci	return status;
5728c2ecf20Sopenharmony_ci}
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_cistatic void parport_mos7715_enable_irq(struct parport *pp)
5758c2ecf20Sopenharmony_ci{
5768c2ecf20Sopenharmony_ci}
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_cistatic void parport_mos7715_disable_irq(struct parport *pp)
5798c2ecf20Sopenharmony_ci{
5808c2ecf20Sopenharmony_ci}
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_cistatic void parport_mos7715_data_forward(struct parport *pp)
5838c2ecf20Sopenharmony_ci{
5848c2ecf20Sopenharmony_ci	struct mos7715_parport *mos_parport = pp->private_data;
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	if (parport_prologue(pp) < 0)
5878c2ecf20Sopenharmony_ci		return;
5888c2ecf20Sopenharmony_ci	mos7715_change_mode(mos_parport, PS2);
5898c2ecf20Sopenharmony_ci	mos_parport->shadowDCR &=  ~0x20;
5908c2ecf20Sopenharmony_ci	write_mos_reg(mos_parport->serial, dummy, MOS7720_DCR,
5918c2ecf20Sopenharmony_ci		      mos_parport->shadowDCR);
5928c2ecf20Sopenharmony_ci	parport_epilogue(pp);
5938c2ecf20Sopenharmony_ci}
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_cistatic void parport_mos7715_data_reverse(struct parport *pp)
5968c2ecf20Sopenharmony_ci{
5978c2ecf20Sopenharmony_ci	struct mos7715_parport *mos_parport = pp->private_data;
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	if (parport_prologue(pp) < 0)
6008c2ecf20Sopenharmony_ci		return;
6018c2ecf20Sopenharmony_ci	mos7715_change_mode(mos_parport, PS2);
6028c2ecf20Sopenharmony_ci	mos_parport->shadowDCR |= 0x20;
6038c2ecf20Sopenharmony_ci	write_mos_reg(mos_parport->serial, dummy, MOS7720_DCR,
6048c2ecf20Sopenharmony_ci		      mos_parport->shadowDCR);
6058c2ecf20Sopenharmony_ci	parport_epilogue(pp);
6068c2ecf20Sopenharmony_ci}
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_cistatic void parport_mos7715_init_state(struct pardevice *dev,
6098c2ecf20Sopenharmony_ci				       struct parport_state *s)
6108c2ecf20Sopenharmony_ci{
6118c2ecf20Sopenharmony_ci	s->u.pc.ctr = DCR_INIT_VAL;
6128c2ecf20Sopenharmony_ci	s->u.pc.ecr = ECR_INIT_VAL;
6138c2ecf20Sopenharmony_ci}
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci/* N.B. Parport core code requires that this function not block */
6168c2ecf20Sopenharmony_cistatic void parport_mos7715_save_state(struct parport *pp,
6178c2ecf20Sopenharmony_ci				       struct parport_state *s)
6188c2ecf20Sopenharmony_ci{
6198c2ecf20Sopenharmony_ci	struct mos7715_parport *mos_parport;
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	spin_lock(&release_lock);
6228c2ecf20Sopenharmony_ci	mos_parport = pp->private_data;
6238c2ecf20Sopenharmony_ci	if (unlikely(mos_parport == NULL)) {	/* release called */
6248c2ecf20Sopenharmony_ci		spin_unlock(&release_lock);
6258c2ecf20Sopenharmony_ci		return;
6268c2ecf20Sopenharmony_ci	}
6278c2ecf20Sopenharmony_ci	s->u.pc.ctr = mos_parport->shadowDCR;
6288c2ecf20Sopenharmony_ci	s->u.pc.ecr = mos_parport->shadowECR;
6298c2ecf20Sopenharmony_ci	spin_unlock(&release_lock);
6308c2ecf20Sopenharmony_ci}
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci/* N.B. Parport core code requires that this function not block */
6338c2ecf20Sopenharmony_cistatic void parport_mos7715_restore_state(struct parport *pp,
6348c2ecf20Sopenharmony_ci					  struct parport_state *s)
6358c2ecf20Sopenharmony_ci{
6368c2ecf20Sopenharmony_ci	struct mos7715_parport *mos_parport;
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	spin_lock(&release_lock);
6398c2ecf20Sopenharmony_ci	mos_parport = pp->private_data;
6408c2ecf20Sopenharmony_ci	if (unlikely(mos_parport == NULL)) {	/* release called */
6418c2ecf20Sopenharmony_ci		spin_unlock(&release_lock);
6428c2ecf20Sopenharmony_ci		return;
6438c2ecf20Sopenharmony_ci	}
6448c2ecf20Sopenharmony_ci	mos_parport->shadowDCR = s->u.pc.ctr;
6458c2ecf20Sopenharmony_ci	mos_parport->shadowECR = s->u.pc.ecr;
6468c2ecf20Sopenharmony_ci	write_parport_reg_nonblock(mos_parport, MOS7720_DCR,
6478c2ecf20Sopenharmony_ci				   mos_parport->shadowDCR);
6488c2ecf20Sopenharmony_ci	write_parport_reg_nonblock(mos_parport, MOS7720_ECR,
6498c2ecf20Sopenharmony_ci				   mos_parport->shadowECR);
6508c2ecf20Sopenharmony_ci	spin_unlock(&release_lock);
6518c2ecf20Sopenharmony_ci}
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_cistatic size_t parport_mos7715_write_compat(struct parport *pp,
6548c2ecf20Sopenharmony_ci					   const void *buffer,
6558c2ecf20Sopenharmony_ci					   size_t len, int flags)
6568c2ecf20Sopenharmony_ci{
6578c2ecf20Sopenharmony_ci	int retval;
6588c2ecf20Sopenharmony_ci	struct mos7715_parport *mos_parport = pp->private_data;
6598c2ecf20Sopenharmony_ci	int actual_len;
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	if (parport_prologue(pp) < 0)
6628c2ecf20Sopenharmony_ci		return 0;
6638c2ecf20Sopenharmony_ci	mos7715_change_mode(mos_parport, PPF);
6648c2ecf20Sopenharmony_ci	retval = usb_bulk_msg(mos_parport->serial->dev,
6658c2ecf20Sopenharmony_ci			      usb_sndbulkpipe(mos_parport->serial->dev, 2),
6668c2ecf20Sopenharmony_ci			      (void *)buffer, len, &actual_len,
6678c2ecf20Sopenharmony_ci			      MOS_WDR_TIMEOUT);
6688c2ecf20Sopenharmony_ci	parport_epilogue(pp);
6698c2ecf20Sopenharmony_ci	if (retval) {
6708c2ecf20Sopenharmony_ci		dev_err(&mos_parport->serial->dev->dev,
6718c2ecf20Sopenharmony_ci			"mos7720: usb_bulk_msg() failed: %d\n", retval);
6728c2ecf20Sopenharmony_ci		return 0;
6738c2ecf20Sopenharmony_ci	}
6748c2ecf20Sopenharmony_ci	return actual_len;
6758c2ecf20Sopenharmony_ci}
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_cistatic struct parport_operations parport_mos7715_ops = {
6788c2ecf20Sopenharmony_ci	.owner =		THIS_MODULE,
6798c2ecf20Sopenharmony_ci	.write_data =		parport_mos7715_write_data,
6808c2ecf20Sopenharmony_ci	.read_data =		parport_mos7715_read_data,
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	.write_control =	parport_mos7715_write_control,
6838c2ecf20Sopenharmony_ci	.read_control =		parport_mos7715_read_control,
6848c2ecf20Sopenharmony_ci	.frob_control =		parport_mos7715_frob_control,
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	.read_status =		parport_mos7715_read_status,
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	.enable_irq =		parport_mos7715_enable_irq,
6898c2ecf20Sopenharmony_ci	.disable_irq =		parport_mos7715_disable_irq,
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	.data_forward =		parport_mos7715_data_forward,
6928c2ecf20Sopenharmony_ci	.data_reverse =		parport_mos7715_data_reverse,
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci	.init_state =		parport_mos7715_init_state,
6958c2ecf20Sopenharmony_ci	.save_state =		parport_mos7715_save_state,
6968c2ecf20Sopenharmony_ci	.restore_state =	parport_mos7715_restore_state,
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	.compat_write_data =	parport_mos7715_write_compat,
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	.nibble_read_data =	parport_ieee1284_read_nibble,
7018c2ecf20Sopenharmony_ci	.byte_read_data =	parport_ieee1284_read_byte,
7028c2ecf20Sopenharmony_ci};
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci/*
7058c2ecf20Sopenharmony_ci * Allocate and initialize parallel port control struct, initialize
7068c2ecf20Sopenharmony_ci * the parallel port hardware device, and register with the parport subsystem.
7078c2ecf20Sopenharmony_ci */
7088c2ecf20Sopenharmony_cistatic int mos7715_parport_init(struct usb_serial *serial)
7098c2ecf20Sopenharmony_ci{
7108c2ecf20Sopenharmony_ci	struct mos7715_parport *mos_parport;
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	/* allocate and initialize parallel port control struct */
7138c2ecf20Sopenharmony_ci	mos_parport = kzalloc(sizeof(struct mos7715_parport), GFP_KERNEL);
7148c2ecf20Sopenharmony_ci	if (!mos_parport)
7158c2ecf20Sopenharmony_ci		return -ENOMEM;
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	mos_parport->msg_pending = false;
7188c2ecf20Sopenharmony_ci	kref_init(&mos_parport->ref_count);
7198c2ecf20Sopenharmony_ci	spin_lock_init(&mos_parport->listlock);
7208c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&mos_parport->active_urbs);
7218c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&mos_parport->deferred_urbs);
7228c2ecf20Sopenharmony_ci	usb_set_serial_data(serial, mos_parport); /* hijack private pointer */
7238c2ecf20Sopenharmony_ci	mos_parport->serial = serial;
7248c2ecf20Sopenharmony_ci	tasklet_setup(&mos_parport->urb_tasklet, send_deferred_urbs);
7258c2ecf20Sopenharmony_ci	init_completion(&mos_parport->syncmsg_compl);
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	/* cycle parallel port reset bit */
7288c2ecf20Sopenharmony_ci	write_mos_reg(mos_parport->serial, dummy, MOS7720_PP_REG, (__u8)0x80);
7298c2ecf20Sopenharmony_ci	write_mos_reg(mos_parport->serial, dummy, MOS7720_PP_REG, (__u8)0x00);
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	/* initialize device registers */
7328c2ecf20Sopenharmony_ci	mos_parport->shadowDCR = DCR_INIT_VAL;
7338c2ecf20Sopenharmony_ci	write_mos_reg(mos_parport->serial, dummy, MOS7720_DCR,
7348c2ecf20Sopenharmony_ci		      mos_parport->shadowDCR);
7358c2ecf20Sopenharmony_ci	mos_parport->shadowECR = ECR_INIT_VAL;
7368c2ecf20Sopenharmony_ci	write_mos_reg(mos_parport->serial, dummy, MOS7720_ECR,
7378c2ecf20Sopenharmony_ci		      mos_parport->shadowECR);
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci	/* register with parport core */
7408c2ecf20Sopenharmony_ci	mos_parport->pp = parport_register_port(0, PARPORT_IRQ_NONE,
7418c2ecf20Sopenharmony_ci						PARPORT_DMA_NONE,
7428c2ecf20Sopenharmony_ci						&parport_mos7715_ops);
7438c2ecf20Sopenharmony_ci	if (mos_parport->pp == NULL) {
7448c2ecf20Sopenharmony_ci		dev_err(&serial->interface->dev,
7458c2ecf20Sopenharmony_ci			"Could not register parport\n");
7468c2ecf20Sopenharmony_ci		kref_put(&mos_parport->ref_count, destroy_mos_parport);
7478c2ecf20Sopenharmony_ci		return -EIO;
7488c2ecf20Sopenharmony_ci	}
7498c2ecf20Sopenharmony_ci	mos_parport->pp->private_data = mos_parport;
7508c2ecf20Sopenharmony_ci	mos_parport->pp->modes = PARPORT_MODE_COMPAT | PARPORT_MODE_PCSPP;
7518c2ecf20Sopenharmony_ci	mos_parport->pp->dev = &serial->interface->dev;
7528c2ecf20Sopenharmony_ci	parport_announce_port(mos_parport->pp);
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	return 0;
7558c2ecf20Sopenharmony_ci}
7568c2ecf20Sopenharmony_ci#endif	/* CONFIG_USB_SERIAL_MOS7715_PARPORT */
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci/*
7598c2ecf20Sopenharmony_ci * mos7720_interrupt_callback
7608c2ecf20Sopenharmony_ci *	this is the callback function for when we have received data on the
7618c2ecf20Sopenharmony_ci *	interrupt endpoint.
7628c2ecf20Sopenharmony_ci */
7638c2ecf20Sopenharmony_cistatic void mos7720_interrupt_callback(struct urb *urb)
7648c2ecf20Sopenharmony_ci{
7658c2ecf20Sopenharmony_ci	int result;
7668c2ecf20Sopenharmony_ci	int length;
7678c2ecf20Sopenharmony_ci	int status = urb->status;
7688c2ecf20Sopenharmony_ci	struct device *dev = &urb->dev->dev;
7698c2ecf20Sopenharmony_ci	__u8 *data;
7708c2ecf20Sopenharmony_ci	__u8 sp1;
7718c2ecf20Sopenharmony_ci	__u8 sp2;
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	switch (status) {
7748c2ecf20Sopenharmony_ci	case 0:
7758c2ecf20Sopenharmony_ci		/* success */
7768c2ecf20Sopenharmony_ci		break;
7778c2ecf20Sopenharmony_ci	case -ECONNRESET:
7788c2ecf20Sopenharmony_ci	case -ENOENT:
7798c2ecf20Sopenharmony_ci	case -ESHUTDOWN:
7808c2ecf20Sopenharmony_ci		/* this urb is terminated, clean up */
7818c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - urb shutting down with status: %d\n", __func__, status);
7828c2ecf20Sopenharmony_ci		return;
7838c2ecf20Sopenharmony_ci	default:
7848c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - nonzero urb status received: %d\n", __func__, status);
7858c2ecf20Sopenharmony_ci		goto exit;
7868c2ecf20Sopenharmony_ci	}
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	length = urb->actual_length;
7898c2ecf20Sopenharmony_ci	data = urb->transfer_buffer;
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	/* Moschip get 4 bytes
7928c2ecf20Sopenharmony_ci	 * Byte 1 IIR Port 1 (port.number is 0)
7938c2ecf20Sopenharmony_ci	 * Byte 2 IIR Port 2 (port.number is 1)
7948c2ecf20Sopenharmony_ci	 * Byte 3 --------------
7958c2ecf20Sopenharmony_ci	 * Byte 4 FIFO status for both */
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	/* the above description is inverted
7988c2ecf20Sopenharmony_ci	 * 	oneukum 2007-03-14 */
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	if (unlikely(length != 4)) {
8018c2ecf20Sopenharmony_ci		dev_dbg(dev, "Wrong data !!!\n");
8028c2ecf20Sopenharmony_ci		return;
8038c2ecf20Sopenharmony_ci	}
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	sp1 = data[3];
8068c2ecf20Sopenharmony_ci	sp2 = data[2];
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	if ((sp1 | sp2) & 0x01) {
8098c2ecf20Sopenharmony_ci		/* No Interrupt Pending in both the ports */
8108c2ecf20Sopenharmony_ci		dev_dbg(dev, "No Interrupt !!!\n");
8118c2ecf20Sopenharmony_ci	} else {
8128c2ecf20Sopenharmony_ci		switch (sp1 & 0x0f) {
8138c2ecf20Sopenharmony_ci		case SERIAL_IIR_RLS:
8148c2ecf20Sopenharmony_ci			dev_dbg(dev, "Serial Port 1: Receiver status error or address bit detected in 9-bit mode\n");
8158c2ecf20Sopenharmony_ci			break;
8168c2ecf20Sopenharmony_ci		case SERIAL_IIR_CTI:
8178c2ecf20Sopenharmony_ci			dev_dbg(dev, "Serial Port 1: Receiver time out\n");
8188c2ecf20Sopenharmony_ci			break;
8198c2ecf20Sopenharmony_ci		case SERIAL_IIR_MS:
8208c2ecf20Sopenharmony_ci			/* dev_dbg(dev, "Serial Port 1: Modem status change\n"); */
8218c2ecf20Sopenharmony_ci			break;
8228c2ecf20Sopenharmony_ci		}
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci		switch (sp2 & 0x0f) {
8258c2ecf20Sopenharmony_ci		case SERIAL_IIR_RLS:
8268c2ecf20Sopenharmony_ci			dev_dbg(dev, "Serial Port 2: Receiver status error or address bit detected in 9-bit mode\n");
8278c2ecf20Sopenharmony_ci			break;
8288c2ecf20Sopenharmony_ci		case SERIAL_IIR_CTI:
8298c2ecf20Sopenharmony_ci			dev_dbg(dev, "Serial Port 2: Receiver time out\n");
8308c2ecf20Sopenharmony_ci			break;
8318c2ecf20Sopenharmony_ci		case SERIAL_IIR_MS:
8328c2ecf20Sopenharmony_ci			/* dev_dbg(dev, "Serial Port 2: Modem status change\n"); */
8338c2ecf20Sopenharmony_ci			break;
8348c2ecf20Sopenharmony_ci		}
8358c2ecf20Sopenharmony_ci	}
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ciexit:
8388c2ecf20Sopenharmony_ci	result = usb_submit_urb(urb, GFP_ATOMIC);
8398c2ecf20Sopenharmony_ci	if (result)
8408c2ecf20Sopenharmony_ci		dev_err(dev, "%s - Error %d submitting control urb\n", __func__, result);
8418c2ecf20Sopenharmony_ci}
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci/*
8448c2ecf20Sopenharmony_ci * mos7715_interrupt_callback
8458c2ecf20Sopenharmony_ci *	this is the 7715's callback function for when we have received data on
8468c2ecf20Sopenharmony_ci *	the interrupt endpoint.
8478c2ecf20Sopenharmony_ci */
8488c2ecf20Sopenharmony_cistatic void mos7715_interrupt_callback(struct urb *urb)
8498c2ecf20Sopenharmony_ci{
8508c2ecf20Sopenharmony_ci	int result;
8518c2ecf20Sopenharmony_ci	int length;
8528c2ecf20Sopenharmony_ci	int status = urb->status;
8538c2ecf20Sopenharmony_ci	struct device *dev = &urb->dev->dev;
8548c2ecf20Sopenharmony_ci	__u8 *data;
8558c2ecf20Sopenharmony_ci	__u8 iir;
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci	switch (status) {
8588c2ecf20Sopenharmony_ci	case 0:
8598c2ecf20Sopenharmony_ci		/* success */
8608c2ecf20Sopenharmony_ci		break;
8618c2ecf20Sopenharmony_ci	case -ECONNRESET:
8628c2ecf20Sopenharmony_ci	case -ENOENT:
8638c2ecf20Sopenharmony_ci	case -ESHUTDOWN:
8648c2ecf20Sopenharmony_ci	case -ENODEV:
8658c2ecf20Sopenharmony_ci		/* this urb is terminated, clean up */
8668c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - urb shutting down with status: %d\n", __func__, status);
8678c2ecf20Sopenharmony_ci		return;
8688c2ecf20Sopenharmony_ci	default:
8698c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - nonzero urb status received: %d\n", __func__, status);
8708c2ecf20Sopenharmony_ci		goto exit;
8718c2ecf20Sopenharmony_ci	}
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci	length = urb->actual_length;
8748c2ecf20Sopenharmony_ci	data = urb->transfer_buffer;
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci	/* Structure of data from 7715 device:
8778c2ecf20Sopenharmony_ci	 * Byte 1: IIR serial Port
8788c2ecf20Sopenharmony_ci	 * Byte 2: unused
8798c2ecf20Sopenharmony_ci	 * Byte 2: DSR parallel port
8808c2ecf20Sopenharmony_ci	 * Byte 4: FIFO status for both */
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	if (unlikely(length != 4)) {
8838c2ecf20Sopenharmony_ci		dev_dbg(dev, "Wrong data !!!\n");
8848c2ecf20Sopenharmony_ci		return;
8858c2ecf20Sopenharmony_ci	}
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	iir = data[0];
8888c2ecf20Sopenharmony_ci	if (!(iir & 0x01)) {	/* serial port interrupt pending */
8898c2ecf20Sopenharmony_ci		switch (iir & 0x0f) {
8908c2ecf20Sopenharmony_ci		case SERIAL_IIR_RLS:
8918c2ecf20Sopenharmony_ci			dev_dbg(dev, "Serial Port: Receiver status error or address bit detected in 9-bit mode\n");
8928c2ecf20Sopenharmony_ci			break;
8938c2ecf20Sopenharmony_ci		case SERIAL_IIR_CTI:
8948c2ecf20Sopenharmony_ci			dev_dbg(dev, "Serial Port: Receiver time out\n");
8958c2ecf20Sopenharmony_ci			break;
8968c2ecf20Sopenharmony_ci		case SERIAL_IIR_MS:
8978c2ecf20Sopenharmony_ci			/* dev_dbg(dev, "Serial Port: Modem status change\n"); */
8988c2ecf20Sopenharmony_ci			break;
8998c2ecf20Sopenharmony_ci		}
9008c2ecf20Sopenharmony_ci	}
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
9038c2ecf20Sopenharmony_ci	{       /* update local copy of DSR reg */
9048c2ecf20Sopenharmony_ci		struct usb_serial_port *port = urb->context;
9058c2ecf20Sopenharmony_ci		struct mos7715_parport *mos_parport = port->serial->private;
9068c2ecf20Sopenharmony_ci		if (unlikely(mos_parport == NULL))
9078c2ecf20Sopenharmony_ci			return;
9088c2ecf20Sopenharmony_ci		atomic_set(&mos_parport->shadowDSR, data[2]);
9098c2ecf20Sopenharmony_ci	}
9108c2ecf20Sopenharmony_ci#endif
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ciexit:
9138c2ecf20Sopenharmony_ci	result = usb_submit_urb(urb, GFP_ATOMIC);
9148c2ecf20Sopenharmony_ci	if (result)
9158c2ecf20Sopenharmony_ci		dev_err(dev, "%s - Error %d submitting control urb\n", __func__, result);
9168c2ecf20Sopenharmony_ci}
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_ci/*
9198c2ecf20Sopenharmony_ci * mos7720_bulk_in_callback
9208c2ecf20Sopenharmony_ci *	this is the callback function for when we have received data on the
9218c2ecf20Sopenharmony_ci *	bulk in endpoint.
9228c2ecf20Sopenharmony_ci */
9238c2ecf20Sopenharmony_cistatic void mos7720_bulk_in_callback(struct urb *urb)
9248c2ecf20Sopenharmony_ci{
9258c2ecf20Sopenharmony_ci	int retval;
9268c2ecf20Sopenharmony_ci	unsigned char *data ;
9278c2ecf20Sopenharmony_ci	struct usb_serial_port *port;
9288c2ecf20Sopenharmony_ci	int status = urb->status;
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_ci	if (status) {
9318c2ecf20Sopenharmony_ci		dev_dbg(&urb->dev->dev, "nonzero read bulk status received: %d\n", status);
9328c2ecf20Sopenharmony_ci		return;
9338c2ecf20Sopenharmony_ci	}
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci	port = urb->context;
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci	dev_dbg(&port->dev, "Entering...%s\n", __func__);
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci	data = urb->transfer_buffer;
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci	if (urb->actual_length) {
9428c2ecf20Sopenharmony_ci		tty_insert_flip_string(&port->port, data, urb->actual_length);
9438c2ecf20Sopenharmony_ci		tty_flip_buffer_push(&port->port);
9448c2ecf20Sopenharmony_ci	}
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	if (port->read_urb->status != -EINPROGRESS) {
9478c2ecf20Sopenharmony_ci		retval = usb_submit_urb(port->read_urb, GFP_ATOMIC);
9488c2ecf20Sopenharmony_ci		if (retval)
9498c2ecf20Sopenharmony_ci			dev_dbg(&port->dev, "usb_submit_urb(read bulk) failed, retval = %d\n", retval);
9508c2ecf20Sopenharmony_ci	}
9518c2ecf20Sopenharmony_ci}
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci/*
9548c2ecf20Sopenharmony_ci * mos7720_bulk_out_data_callback
9558c2ecf20Sopenharmony_ci *	this is the callback function for when we have finished sending serial
9568c2ecf20Sopenharmony_ci *	data on the bulk out endpoint.
9578c2ecf20Sopenharmony_ci */
9588c2ecf20Sopenharmony_cistatic void mos7720_bulk_out_data_callback(struct urb *urb)
9598c2ecf20Sopenharmony_ci{
9608c2ecf20Sopenharmony_ci	struct moschip_port *mos7720_port;
9618c2ecf20Sopenharmony_ci	int status = urb->status;
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	if (status) {
9648c2ecf20Sopenharmony_ci		dev_dbg(&urb->dev->dev, "nonzero write bulk status received:%d\n", status);
9658c2ecf20Sopenharmony_ci		return;
9668c2ecf20Sopenharmony_ci	}
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	mos7720_port = urb->context;
9698c2ecf20Sopenharmony_ci	if (!mos7720_port) {
9708c2ecf20Sopenharmony_ci		dev_dbg(&urb->dev->dev, "NULL mos7720_port pointer\n");
9718c2ecf20Sopenharmony_ci		return ;
9728c2ecf20Sopenharmony_ci	}
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci	if (mos7720_port->open)
9758c2ecf20Sopenharmony_ci		tty_port_tty_wakeup(&mos7720_port->port->port);
9768c2ecf20Sopenharmony_ci}
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_cistatic int mos77xx_calc_num_ports(struct usb_serial *serial,
9798c2ecf20Sopenharmony_ci					struct usb_serial_endpoints *epds)
9808c2ecf20Sopenharmony_ci{
9818c2ecf20Sopenharmony_ci	u16 product = le16_to_cpu(serial->dev->descriptor.idProduct);
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	if (product == MOSCHIP_DEVICE_ID_7715) {
9848c2ecf20Sopenharmony_ci		/*
9858c2ecf20Sopenharmony_ci		 * The 7715 uses the first bulk in/out endpoint pair for the
9868c2ecf20Sopenharmony_ci		 * parallel port, and the second for the serial port. We swap
9878c2ecf20Sopenharmony_ci		 * the endpoint descriptors here so that the the first and
9888c2ecf20Sopenharmony_ci		 * only registered port structure uses the serial-port
9898c2ecf20Sopenharmony_ci		 * endpoints.
9908c2ecf20Sopenharmony_ci		 */
9918c2ecf20Sopenharmony_ci		swap(epds->bulk_in[0], epds->bulk_in[1]);
9928c2ecf20Sopenharmony_ci		swap(epds->bulk_out[0], epds->bulk_out[1]);
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci		return 1;
9958c2ecf20Sopenharmony_ci	}
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	return 2;
9988c2ecf20Sopenharmony_ci}
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_cistatic int mos7720_open(struct tty_struct *tty, struct usb_serial_port *port)
10018c2ecf20Sopenharmony_ci{
10028c2ecf20Sopenharmony_ci	struct usb_serial *serial;
10038c2ecf20Sopenharmony_ci	struct urb *urb;
10048c2ecf20Sopenharmony_ci	struct moschip_port *mos7720_port;
10058c2ecf20Sopenharmony_ci	int response;
10068c2ecf20Sopenharmony_ci	int port_number;
10078c2ecf20Sopenharmony_ci	__u8 data;
10088c2ecf20Sopenharmony_ci	int allocated_urbs = 0;
10098c2ecf20Sopenharmony_ci	int j;
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_ci	serial = port->serial;
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci	mos7720_port = usb_get_serial_port_data(port);
10148c2ecf20Sopenharmony_ci	if (mos7720_port == NULL)
10158c2ecf20Sopenharmony_ci		return -ENODEV;
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	usb_clear_halt(serial->dev, port->write_urb->pipe);
10188c2ecf20Sopenharmony_ci	usb_clear_halt(serial->dev, port->read_urb->pipe);
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci	/* Initialising the write urb pool */
10218c2ecf20Sopenharmony_ci	for (j = 0; j < NUM_URBS; ++j) {
10228c2ecf20Sopenharmony_ci		urb = usb_alloc_urb(0, GFP_KERNEL);
10238c2ecf20Sopenharmony_ci		mos7720_port->write_urb_pool[j] = urb;
10248c2ecf20Sopenharmony_ci		if (!urb)
10258c2ecf20Sopenharmony_ci			continue;
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci		urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE,
10288c2ecf20Sopenharmony_ci					       GFP_KERNEL);
10298c2ecf20Sopenharmony_ci		if (!urb->transfer_buffer) {
10308c2ecf20Sopenharmony_ci			usb_free_urb(mos7720_port->write_urb_pool[j]);
10318c2ecf20Sopenharmony_ci			mos7720_port->write_urb_pool[j] = NULL;
10328c2ecf20Sopenharmony_ci			continue;
10338c2ecf20Sopenharmony_ci		}
10348c2ecf20Sopenharmony_ci		allocated_urbs++;
10358c2ecf20Sopenharmony_ci	}
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci	if (!allocated_urbs)
10388c2ecf20Sopenharmony_ci		return -ENOMEM;
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci	 /* Initialize MCS7720 -- Write Init values to corresponding Registers
10418c2ecf20Sopenharmony_ci	  *
10428c2ecf20Sopenharmony_ci	  * Register Index
10438c2ecf20Sopenharmony_ci	  * 0 : MOS7720_THR/MOS7720_RHR
10448c2ecf20Sopenharmony_ci	  * 1 : MOS7720_IER
10458c2ecf20Sopenharmony_ci	  * 2 : MOS7720_FCR
10468c2ecf20Sopenharmony_ci	  * 3 : MOS7720_LCR
10478c2ecf20Sopenharmony_ci	  * 4 : MOS7720_MCR
10488c2ecf20Sopenharmony_ci	  * 5 : MOS7720_LSR
10498c2ecf20Sopenharmony_ci	  * 6 : MOS7720_MSR
10508c2ecf20Sopenharmony_ci	  * 7 : MOS7720_SPR
10518c2ecf20Sopenharmony_ci	  *
10528c2ecf20Sopenharmony_ci	  * 0x08 : SP1/2 Control Reg
10538c2ecf20Sopenharmony_ci	  */
10548c2ecf20Sopenharmony_ci	port_number = port->port_number;
10558c2ecf20Sopenharmony_ci	read_mos_reg(serial, port_number, MOS7720_LSR, &data);
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci	dev_dbg(&port->dev, "SS::%p LSR:%x\n", mos7720_port, data);
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci	write_mos_reg(serial, dummy, MOS7720_SP1_REG, 0x02);
10608c2ecf20Sopenharmony_ci	write_mos_reg(serial, dummy, MOS7720_SP2_REG, 0x02);
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_IER, 0x00);
10638c2ecf20Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_FCR, 0x00);
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_FCR, 0xcf);
10668c2ecf20Sopenharmony_ci	mos7720_port->shadowLCR = 0x03;
10678c2ecf20Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_LCR,
10688c2ecf20Sopenharmony_ci		      mos7720_port->shadowLCR);
10698c2ecf20Sopenharmony_ci	mos7720_port->shadowMCR = 0x0b;
10708c2ecf20Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_MCR,
10718c2ecf20Sopenharmony_ci		      mos7720_port->shadowMCR);
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_SP_CONTROL_REG, 0x00);
10748c2ecf20Sopenharmony_ci	read_mos_reg(serial, dummy, MOS7720_SP_CONTROL_REG, &data);
10758c2ecf20Sopenharmony_ci	data = data | (port->port_number + 1);
10768c2ecf20Sopenharmony_ci	write_mos_reg(serial, dummy, MOS7720_SP_CONTROL_REG, data);
10778c2ecf20Sopenharmony_ci	mos7720_port->shadowLCR = 0x83;
10788c2ecf20Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_LCR,
10798c2ecf20Sopenharmony_ci		      mos7720_port->shadowLCR);
10808c2ecf20Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_THR, 0x0c);
10818c2ecf20Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_IER, 0x00);
10828c2ecf20Sopenharmony_ci	mos7720_port->shadowLCR = 0x03;
10838c2ecf20Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_LCR,
10848c2ecf20Sopenharmony_ci		      mos7720_port->shadowLCR);
10858c2ecf20Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_IER, 0x0c);
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci	response = usb_submit_urb(port->read_urb, GFP_KERNEL);
10888c2ecf20Sopenharmony_ci	if (response)
10898c2ecf20Sopenharmony_ci		dev_err(&port->dev, "%s - Error %d submitting read urb\n",
10908c2ecf20Sopenharmony_ci							__func__, response);
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci	/* initialize our port settings */
10938c2ecf20Sopenharmony_ci	mos7720_port->shadowMCR = UART_MCR_OUT2; /* Must set to enable ints! */
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_ci	/* send a open port command */
10968c2ecf20Sopenharmony_ci	mos7720_port->open = 1;
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_ci	return 0;
10998c2ecf20Sopenharmony_ci}
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci/*
11028c2ecf20Sopenharmony_ci * mos7720_chars_in_buffer
11038c2ecf20Sopenharmony_ci *	this function is called by the tty driver when it wants to know how many
11048c2ecf20Sopenharmony_ci *	bytes of data we currently have outstanding in the port (data that has
11058c2ecf20Sopenharmony_ci *	been written, but hasn't made it out the port yet)
11068c2ecf20Sopenharmony_ci *	If successful, we return the number of bytes left to be written in the
11078c2ecf20Sopenharmony_ci *	system,
11088c2ecf20Sopenharmony_ci *	Otherwise we return a negative error number.
11098c2ecf20Sopenharmony_ci */
11108c2ecf20Sopenharmony_cistatic int mos7720_chars_in_buffer(struct tty_struct *tty)
11118c2ecf20Sopenharmony_ci{
11128c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
11138c2ecf20Sopenharmony_ci	int i;
11148c2ecf20Sopenharmony_ci	int chars = 0;
11158c2ecf20Sopenharmony_ci	struct moschip_port *mos7720_port;
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_ci	mos7720_port = usb_get_serial_port_data(port);
11188c2ecf20Sopenharmony_ci	if (mos7720_port == NULL)
11198c2ecf20Sopenharmony_ci		return 0;
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_URBS; ++i) {
11228c2ecf20Sopenharmony_ci		if (mos7720_port->write_urb_pool[i] &&
11238c2ecf20Sopenharmony_ci		    mos7720_port->write_urb_pool[i]->status == -EINPROGRESS)
11248c2ecf20Sopenharmony_ci			chars += URB_TRANSFER_BUFFER_SIZE;
11258c2ecf20Sopenharmony_ci	}
11268c2ecf20Sopenharmony_ci	dev_dbg(&port->dev, "%s - returns %d\n", __func__, chars);
11278c2ecf20Sopenharmony_ci	return chars;
11288c2ecf20Sopenharmony_ci}
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_cistatic void mos7720_close(struct usb_serial_port *port)
11318c2ecf20Sopenharmony_ci{
11328c2ecf20Sopenharmony_ci	struct usb_serial *serial;
11338c2ecf20Sopenharmony_ci	struct moschip_port *mos7720_port;
11348c2ecf20Sopenharmony_ci	int j;
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_ci	serial = port->serial;
11378c2ecf20Sopenharmony_ci
11388c2ecf20Sopenharmony_ci	mos7720_port = usb_get_serial_port_data(port);
11398c2ecf20Sopenharmony_ci	if (mos7720_port == NULL)
11408c2ecf20Sopenharmony_ci		return;
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci	for (j = 0; j < NUM_URBS; ++j)
11438c2ecf20Sopenharmony_ci		usb_kill_urb(mos7720_port->write_urb_pool[j]);
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_ci	/* Freeing Write URBs */
11468c2ecf20Sopenharmony_ci	for (j = 0; j < NUM_URBS; ++j) {
11478c2ecf20Sopenharmony_ci		if (mos7720_port->write_urb_pool[j]) {
11488c2ecf20Sopenharmony_ci			kfree(mos7720_port->write_urb_pool[j]->transfer_buffer);
11498c2ecf20Sopenharmony_ci			usb_free_urb(mos7720_port->write_urb_pool[j]);
11508c2ecf20Sopenharmony_ci		}
11518c2ecf20Sopenharmony_ci	}
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci	/* While closing port, shutdown all bulk read, write  *
11548c2ecf20Sopenharmony_ci	 * and interrupt read if they exists, otherwise nop   */
11558c2ecf20Sopenharmony_ci	usb_kill_urb(port->write_urb);
11568c2ecf20Sopenharmony_ci	usb_kill_urb(port->read_urb);
11578c2ecf20Sopenharmony_ci
11588c2ecf20Sopenharmony_ci	write_mos_reg(serial, port->port_number, MOS7720_MCR, 0x00);
11598c2ecf20Sopenharmony_ci	write_mos_reg(serial, port->port_number, MOS7720_IER, 0x00);
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci	mos7720_port->open = 0;
11628c2ecf20Sopenharmony_ci}
11638c2ecf20Sopenharmony_ci
11648c2ecf20Sopenharmony_cistatic void mos7720_break(struct tty_struct *tty, int break_state)
11658c2ecf20Sopenharmony_ci{
11668c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
11678c2ecf20Sopenharmony_ci	unsigned char data;
11688c2ecf20Sopenharmony_ci	struct usb_serial *serial;
11698c2ecf20Sopenharmony_ci	struct moschip_port *mos7720_port;
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci	serial = port->serial;
11728c2ecf20Sopenharmony_ci
11738c2ecf20Sopenharmony_ci	mos7720_port = usb_get_serial_port_data(port);
11748c2ecf20Sopenharmony_ci	if (mos7720_port == NULL)
11758c2ecf20Sopenharmony_ci		return;
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci	if (break_state == -1)
11788c2ecf20Sopenharmony_ci		data = mos7720_port->shadowLCR | UART_LCR_SBC;
11798c2ecf20Sopenharmony_ci	else
11808c2ecf20Sopenharmony_ci		data = mos7720_port->shadowLCR & ~UART_LCR_SBC;
11818c2ecf20Sopenharmony_ci
11828c2ecf20Sopenharmony_ci	mos7720_port->shadowLCR  = data;
11838c2ecf20Sopenharmony_ci	write_mos_reg(serial, port->port_number, MOS7720_LCR,
11848c2ecf20Sopenharmony_ci		      mos7720_port->shadowLCR);
11858c2ecf20Sopenharmony_ci}
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_ci/*
11888c2ecf20Sopenharmony_ci * mos7720_write_room
11898c2ecf20Sopenharmony_ci *	this function is called by the tty driver when it wants to know how many
11908c2ecf20Sopenharmony_ci *	bytes of data we can accept for a specific port.
11918c2ecf20Sopenharmony_ci *	If successful, we return the amount of room that we have for this port
11928c2ecf20Sopenharmony_ci *	Otherwise we return a negative error number.
11938c2ecf20Sopenharmony_ci */
11948c2ecf20Sopenharmony_cistatic int mos7720_write_room(struct tty_struct *tty)
11958c2ecf20Sopenharmony_ci{
11968c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
11978c2ecf20Sopenharmony_ci	struct moschip_port *mos7720_port;
11988c2ecf20Sopenharmony_ci	int room = 0;
11998c2ecf20Sopenharmony_ci	int i;
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ci	mos7720_port = usb_get_serial_port_data(port);
12028c2ecf20Sopenharmony_ci	if (mos7720_port == NULL)
12038c2ecf20Sopenharmony_ci		return -ENODEV;
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_ci	/* FIXME: Locking */
12068c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_URBS; ++i) {
12078c2ecf20Sopenharmony_ci		if (mos7720_port->write_urb_pool[i] &&
12088c2ecf20Sopenharmony_ci		    mos7720_port->write_urb_pool[i]->status != -EINPROGRESS)
12098c2ecf20Sopenharmony_ci			room += URB_TRANSFER_BUFFER_SIZE;
12108c2ecf20Sopenharmony_ci	}
12118c2ecf20Sopenharmony_ci
12128c2ecf20Sopenharmony_ci	dev_dbg(&port->dev, "%s - returns %d\n", __func__, room);
12138c2ecf20Sopenharmony_ci	return room;
12148c2ecf20Sopenharmony_ci}
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_cistatic int mos7720_write(struct tty_struct *tty, struct usb_serial_port *port,
12178c2ecf20Sopenharmony_ci				 const unsigned char *data, int count)
12188c2ecf20Sopenharmony_ci{
12198c2ecf20Sopenharmony_ci	int status;
12208c2ecf20Sopenharmony_ci	int i;
12218c2ecf20Sopenharmony_ci	int bytes_sent = 0;
12228c2ecf20Sopenharmony_ci	int transfer_size;
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci	struct moschip_port *mos7720_port;
12258c2ecf20Sopenharmony_ci	struct usb_serial *serial;
12268c2ecf20Sopenharmony_ci	struct urb    *urb;
12278c2ecf20Sopenharmony_ci	const unsigned char *current_position = data;
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_ci	serial = port->serial;
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_ci	mos7720_port = usb_get_serial_port_data(port);
12328c2ecf20Sopenharmony_ci	if (mos7720_port == NULL)
12338c2ecf20Sopenharmony_ci		return -ENODEV;
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_ci	/* try to find a free urb in the list */
12368c2ecf20Sopenharmony_ci	urb = NULL;
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_URBS; ++i) {
12398c2ecf20Sopenharmony_ci		if (mos7720_port->write_urb_pool[i] &&
12408c2ecf20Sopenharmony_ci		    mos7720_port->write_urb_pool[i]->status != -EINPROGRESS) {
12418c2ecf20Sopenharmony_ci			urb = mos7720_port->write_urb_pool[i];
12428c2ecf20Sopenharmony_ci			dev_dbg(&port->dev, "URB:%d\n", i);
12438c2ecf20Sopenharmony_ci			break;
12448c2ecf20Sopenharmony_ci		}
12458c2ecf20Sopenharmony_ci	}
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci	if (urb == NULL) {
12488c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - no more free urbs\n", __func__);
12498c2ecf20Sopenharmony_ci		goto exit;
12508c2ecf20Sopenharmony_ci	}
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ci	if (urb->transfer_buffer == NULL) {
12538c2ecf20Sopenharmony_ci		urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE,
12548c2ecf20Sopenharmony_ci					       GFP_ATOMIC);
12558c2ecf20Sopenharmony_ci		if (!urb->transfer_buffer) {
12568c2ecf20Sopenharmony_ci			bytes_sent = -ENOMEM;
12578c2ecf20Sopenharmony_ci			goto exit;
12588c2ecf20Sopenharmony_ci		}
12598c2ecf20Sopenharmony_ci	}
12608c2ecf20Sopenharmony_ci	transfer_size = min(count, URB_TRANSFER_BUFFER_SIZE);
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci	memcpy(urb->transfer_buffer, current_position, transfer_size);
12638c2ecf20Sopenharmony_ci	usb_serial_debug_data(&port->dev, __func__, transfer_size,
12648c2ecf20Sopenharmony_ci			      urb->transfer_buffer);
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ci	/* fill urb with data and submit  */
12678c2ecf20Sopenharmony_ci	usb_fill_bulk_urb(urb, serial->dev,
12688c2ecf20Sopenharmony_ci			  usb_sndbulkpipe(serial->dev,
12698c2ecf20Sopenharmony_ci					port->bulk_out_endpointAddress),
12708c2ecf20Sopenharmony_ci			  urb->transfer_buffer, transfer_size,
12718c2ecf20Sopenharmony_ci			  mos7720_bulk_out_data_callback, mos7720_port);
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_ci	/* send it down the pipe */
12748c2ecf20Sopenharmony_ci	status = usb_submit_urb(urb, GFP_ATOMIC);
12758c2ecf20Sopenharmony_ci	if (status) {
12768c2ecf20Sopenharmony_ci		dev_err_console(port, "%s - usb_submit_urb(write bulk) failed "
12778c2ecf20Sopenharmony_ci			"with status = %d\n", __func__, status);
12788c2ecf20Sopenharmony_ci		bytes_sent = status;
12798c2ecf20Sopenharmony_ci		goto exit;
12808c2ecf20Sopenharmony_ci	}
12818c2ecf20Sopenharmony_ci	bytes_sent = transfer_size;
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ciexit:
12848c2ecf20Sopenharmony_ci	return bytes_sent;
12858c2ecf20Sopenharmony_ci}
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_cistatic void mos7720_throttle(struct tty_struct *tty)
12888c2ecf20Sopenharmony_ci{
12898c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
12908c2ecf20Sopenharmony_ci	struct moschip_port *mos7720_port;
12918c2ecf20Sopenharmony_ci	int status;
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_ci	mos7720_port = usb_get_serial_port_data(port);
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ci	if (mos7720_port == NULL)
12968c2ecf20Sopenharmony_ci		return;
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_ci	if (!mos7720_port->open) {
12998c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - port not opened\n", __func__);
13008c2ecf20Sopenharmony_ci		return;
13018c2ecf20Sopenharmony_ci	}
13028c2ecf20Sopenharmony_ci
13038c2ecf20Sopenharmony_ci	/* if we are implementing XON/XOFF, send the stop character */
13048c2ecf20Sopenharmony_ci	if (I_IXOFF(tty)) {
13058c2ecf20Sopenharmony_ci		unsigned char stop_char = STOP_CHAR(tty);
13068c2ecf20Sopenharmony_ci		status = mos7720_write(tty, port, &stop_char, 1);
13078c2ecf20Sopenharmony_ci		if (status <= 0)
13088c2ecf20Sopenharmony_ci			return;
13098c2ecf20Sopenharmony_ci	}
13108c2ecf20Sopenharmony_ci
13118c2ecf20Sopenharmony_ci	/* if we are implementing RTS/CTS, toggle that line */
13128c2ecf20Sopenharmony_ci	if (C_CRTSCTS(tty)) {
13138c2ecf20Sopenharmony_ci		mos7720_port->shadowMCR &= ~UART_MCR_RTS;
13148c2ecf20Sopenharmony_ci		write_mos_reg(port->serial, port->port_number, MOS7720_MCR,
13158c2ecf20Sopenharmony_ci			      mos7720_port->shadowMCR);
13168c2ecf20Sopenharmony_ci	}
13178c2ecf20Sopenharmony_ci}
13188c2ecf20Sopenharmony_ci
13198c2ecf20Sopenharmony_cistatic void mos7720_unthrottle(struct tty_struct *tty)
13208c2ecf20Sopenharmony_ci{
13218c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
13228c2ecf20Sopenharmony_ci	struct moschip_port *mos7720_port = usb_get_serial_port_data(port);
13238c2ecf20Sopenharmony_ci	int status;
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_ci	if (mos7720_port == NULL)
13268c2ecf20Sopenharmony_ci		return;
13278c2ecf20Sopenharmony_ci
13288c2ecf20Sopenharmony_ci	if (!mos7720_port->open) {
13298c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - port not opened\n", __func__);
13308c2ecf20Sopenharmony_ci		return;
13318c2ecf20Sopenharmony_ci	}
13328c2ecf20Sopenharmony_ci
13338c2ecf20Sopenharmony_ci	/* if we are implementing XON/XOFF, send the start character */
13348c2ecf20Sopenharmony_ci	if (I_IXOFF(tty)) {
13358c2ecf20Sopenharmony_ci		unsigned char start_char = START_CHAR(tty);
13368c2ecf20Sopenharmony_ci		status = mos7720_write(tty, port, &start_char, 1);
13378c2ecf20Sopenharmony_ci		if (status <= 0)
13388c2ecf20Sopenharmony_ci			return;
13398c2ecf20Sopenharmony_ci	}
13408c2ecf20Sopenharmony_ci
13418c2ecf20Sopenharmony_ci	/* if we are implementing RTS/CTS, toggle that line */
13428c2ecf20Sopenharmony_ci	if (C_CRTSCTS(tty)) {
13438c2ecf20Sopenharmony_ci		mos7720_port->shadowMCR |= UART_MCR_RTS;
13448c2ecf20Sopenharmony_ci		write_mos_reg(port->serial, port->port_number, MOS7720_MCR,
13458c2ecf20Sopenharmony_ci			      mos7720_port->shadowMCR);
13468c2ecf20Sopenharmony_ci	}
13478c2ecf20Sopenharmony_ci}
13488c2ecf20Sopenharmony_ci
13498c2ecf20Sopenharmony_ci/* FIXME: this function does not work */
13508c2ecf20Sopenharmony_cistatic int set_higher_rates(struct moschip_port *mos7720_port,
13518c2ecf20Sopenharmony_ci			    unsigned int baud)
13528c2ecf20Sopenharmony_ci{
13538c2ecf20Sopenharmony_ci	struct usb_serial_port *port;
13548c2ecf20Sopenharmony_ci	struct usb_serial *serial;
13558c2ecf20Sopenharmony_ci	int port_number;
13568c2ecf20Sopenharmony_ci	enum mos_regs sp_reg;
13578c2ecf20Sopenharmony_ci	if (mos7720_port == NULL)
13588c2ecf20Sopenharmony_ci		return -EINVAL;
13598c2ecf20Sopenharmony_ci
13608c2ecf20Sopenharmony_ci	port = mos7720_port->port;
13618c2ecf20Sopenharmony_ci	serial = port->serial;
13628c2ecf20Sopenharmony_ci
13638c2ecf20Sopenharmony_ci	 /***********************************************
13648c2ecf20Sopenharmony_ci	 *      Init Sequence for higher rates
13658c2ecf20Sopenharmony_ci	 ***********************************************/
13668c2ecf20Sopenharmony_ci	dev_dbg(&port->dev, "Sending Setting Commands ..........\n");
13678c2ecf20Sopenharmony_ci	port_number = port->port_number;
13688c2ecf20Sopenharmony_ci
13698c2ecf20Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_IER, 0x00);
13708c2ecf20Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_FCR, 0x00);
13718c2ecf20Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_FCR, 0xcf);
13728c2ecf20Sopenharmony_ci	mos7720_port->shadowMCR = 0x0b;
13738c2ecf20Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_MCR,
13748c2ecf20Sopenharmony_ci		      mos7720_port->shadowMCR);
13758c2ecf20Sopenharmony_ci	write_mos_reg(serial, dummy, MOS7720_SP_CONTROL_REG, 0x00);
13768c2ecf20Sopenharmony_ci
13778c2ecf20Sopenharmony_ci	/***********************************************
13788c2ecf20Sopenharmony_ci	 *              Set for higher rates           *
13798c2ecf20Sopenharmony_ci	 ***********************************************/
13808c2ecf20Sopenharmony_ci	/* writing baud rate verbatum into uart clock field clearly not right */
13818c2ecf20Sopenharmony_ci	if (port_number == 0)
13828c2ecf20Sopenharmony_ci		sp_reg = MOS7720_SP1_REG;
13838c2ecf20Sopenharmony_ci	else
13848c2ecf20Sopenharmony_ci		sp_reg = MOS7720_SP2_REG;
13858c2ecf20Sopenharmony_ci	write_mos_reg(serial, dummy, sp_reg, baud * 0x10);
13868c2ecf20Sopenharmony_ci	write_mos_reg(serial, dummy, MOS7720_SP_CONTROL_REG, 0x03);
13878c2ecf20Sopenharmony_ci	mos7720_port->shadowMCR = 0x2b;
13888c2ecf20Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_MCR,
13898c2ecf20Sopenharmony_ci		      mos7720_port->shadowMCR);
13908c2ecf20Sopenharmony_ci
13918c2ecf20Sopenharmony_ci	/***********************************************
13928c2ecf20Sopenharmony_ci	 *              Set DLL/DLM
13938c2ecf20Sopenharmony_ci	 ***********************************************/
13948c2ecf20Sopenharmony_ci	mos7720_port->shadowLCR = mos7720_port->shadowLCR | UART_LCR_DLAB;
13958c2ecf20Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_LCR,
13968c2ecf20Sopenharmony_ci		      mos7720_port->shadowLCR);
13978c2ecf20Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_DLL, 0x01);
13988c2ecf20Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_DLM, 0x00);
13998c2ecf20Sopenharmony_ci	mos7720_port->shadowLCR = mos7720_port->shadowLCR & ~UART_LCR_DLAB;
14008c2ecf20Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_LCR,
14018c2ecf20Sopenharmony_ci		      mos7720_port->shadowLCR);
14028c2ecf20Sopenharmony_ci
14038c2ecf20Sopenharmony_ci	return 0;
14048c2ecf20Sopenharmony_ci}
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_ci/* baud rate information */
14078c2ecf20Sopenharmony_cistruct divisor_table_entry {
14088c2ecf20Sopenharmony_ci	__u32  baudrate;
14098c2ecf20Sopenharmony_ci	__u16  divisor;
14108c2ecf20Sopenharmony_ci};
14118c2ecf20Sopenharmony_ci
14128c2ecf20Sopenharmony_ci/* Define table of divisors for moschip 7720 hardware	   *
14138c2ecf20Sopenharmony_ci * These assume a 3.6864MHz crystal, the standard /16, and *
14148c2ecf20Sopenharmony_ci * MCR.7 = 0.						   */
14158c2ecf20Sopenharmony_cistatic const struct divisor_table_entry divisor_table[] = {
14168c2ecf20Sopenharmony_ci	{   50,		2304},
14178c2ecf20Sopenharmony_ci	{   110,	1047},	/* 2094.545455 => 230450   => .0217 % over */
14188c2ecf20Sopenharmony_ci	{   134,	857},	/* 1713.011152 => 230398.5 => .00065% under */
14198c2ecf20Sopenharmony_ci	{   150,	768},
14208c2ecf20Sopenharmony_ci	{   300,	384},
14218c2ecf20Sopenharmony_ci	{   600,	192},
14228c2ecf20Sopenharmony_ci	{   1200,	96},
14238c2ecf20Sopenharmony_ci	{   1800,	64},
14248c2ecf20Sopenharmony_ci	{   2400,	48},
14258c2ecf20Sopenharmony_ci	{   4800,	24},
14268c2ecf20Sopenharmony_ci	{   7200,	16},
14278c2ecf20Sopenharmony_ci	{   9600,	12},
14288c2ecf20Sopenharmony_ci	{   19200,	6},
14298c2ecf20Sopenharmony_ci	{   38400,	3},
14308c2ecf20Sopenharmony_ci	{   57600,	2},
14318c2ecf20Sopenharmony_ci	{   115200,	1},
14328c2ecf20Sopenharmony_ci};
14338c2ecf20Sopenharmony_ci
14348c2ecf20Sopenharmony_ci/*****************************************************************************
14358c2ecf20Sopenharmony_ci * calc_baud_rate_divisor
14368c2ecf20Sopenharmony_ci *	this function calculates the proper baud rate divisor for the specified
14378c2ecf20Sopenharmony_ci *	baud rate.
14388c2ecf20Sopenharmony_ci *****************************************************************************/
14398c2ecf20Sopenharmony_cistatic int calc_baud_rate_divisor(struct usb_serial_port *port, int baudrate, int *divisor)
14408c2ecf20Sopenharmony_ci{
14418c2ecf20Sopenharmony_ci	int i;
14428c2ecf20Sopenharmony_ci	__u16 custom;
14438c2ecf20Sopenharmony_ci	__u16 round1;
14448c2ecf20Sopenharmony_ci	__u16 round;
14458c2ecf20Sopenharmony_ci
14468c2ecf20Sopenharmony_ci
14478c2ecf20Sopenharmony_ci	dev_dbg(&port->dev, "%s - %d\n", __func__, baudrate);
14488c2ecf20Sopenharmony_ci
14498c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(divisor_table); i++) {
14508c2ecf20Sopenharmony_ci		if (divisor_table[i].baudrate == baudrate) {
14518c2ecf20Sopenharmony_ci			*divisor = divisor_table[i].divisor;
14528c2ecf20Sopenharmony_ci			return 0;
14538c2ecf20Sopenharmony_ci		}
14548c2ecf20Sopenharmony_ci	}
14558c2ecf20Sopenharmony_ci
14568c2ecf20Sopenharmony_ci	/* After trying for all the standard baud rates    *
14578c2ecf20Sopenharmony_ci	 * Try calculating the divisor for this baud rate  */
14588c2ecf20Sopenharmony_ci	if (baudrate > 75 &&  baudrate < 230400) {
14598c2ecf20Sopenharmony_ci		/* get the divisor */
14608c2ecf20Sopenharmony_ci		custom = (__u16)(230400L  / baudrate);
14618c2ecf20Sopenharmony_ci
14628c2ecf20Sopenharmony_ci		/* Check for round off */
14638c2ecf20Sopenharmony_ci		round1 = (__u16)(2304000L / baudrate);
14648c2ecf20Sopenharmony_ci		round = (__u16)(round1 - (custom * 10));
14658c2ecf20Sopenharmony_ci		if (round > 4)
14668c2ecf20Sopenharmony_ci			custom++;
14678c2ecf20Sopenharmony_ci		*divisor = custom;
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "Baud %d = %d\n", baudrate, custom);
14708c2ecf20Sopenharmony_ci		return 0;
14718c2ecf20Sopenharmony_ci	}
14728c2ecf20Sopenharmony_ci
14738c2ecf20Sopenharmony_ci	dev_dbg(&port->dev, "Baud calculation Failed...\n");
14748c2ecf20Sopenharmony_ci	return -EINVAL;
14758c2ecf20Sopenharmony_ci}
14768c2ecf20Sopenharmony_ci
14778c2ecf20Sopenharmony_ci/*
14788c2ecf20Sopenharmony_ci * send_cmd_write_baud_rate
14798c2ecf20Sopenharmony_ci *	this function sends the proper command to change the baud rate of the
14808c2ecf20Sopenharmony_ci *	specified port.
14818c2ecf20Sopenharmony_ci */
14828c2ecf20Sopenharmony_cistatic int send_cmd_write_baud_rate(struct moschip_port *mos7720_port,
14838c2ecf20Sopenharmony_ci				    int baudrate)
14848c2ecf20Sopenharmony_ci{
14858c2ecf20Sopenharmony_ci	struct usb_serial_port *port;
14868c2ecf20Sopenharmony_ci	struct usb_serial *serial;
14878c2ecf20Sopenharmony_ci	int divisor;
14888c2ecf20Sopenharmony_ci	int status;
14898c2ecf20Sopenharmony_ci	unsigned char number;
14908c2ecf20Sopenharmony_ci
14918c2ecf20Sopenharmony_ci	if (mos7720_port == NULL)
14928c2ecf20Sopenharmony_ci		return -1;
14938c2ecf20Sopenharmony_ci
14948c2ecf20Sopenharmony_ci	port = mos7720_port->port;
14958c2ecf20Sopenharmony_ci	serial = port->serial;
14968c2ecf20Sopenharmony_ci
14978c2ecf20Sopenharmony_ci	number = port->port_number;
14988c2ecf20Sopenharmony_ci	dev_dbg(&port->dev, "%s - baud = %d\n", __func__, baudrate);
14998c2ecf20Sopenharmony_ci
15008c2ecf20Sopenharmony_ci	/* Calculate the Divisor */
15018c2ecf20Sopenharmony_ci	status = calc_baud_rate_divisor(port, baudrate, &divisor);
15028c2ecf20Sopenharmony_ci	if (status) {
15038c2ecf20Sopenharmony_ci		dev_err(&port->dev, "%s - bad baud rate\n", __func__);
15048c2ecf20Sopenharmony_ci		return status;
15058c2ecf20Sopenharmony_ci	}
15068c2ecf20Sopenharmony_ci
15078c2ecf20Sopenharmony_ci	/* Enable access to divisor latch */
15088c2ecf20Sopenharmony_ci	mos7720_port->shadowLCR = mos7720_port->shadowLCR | UART_LCR_DLAB;
15098c2ecf20Sopenharmony_ci	write_mos_reg(serial, number, MOS7720_LCR, mos7720_port->shadowLCR);
15108c2ecf20Sopenharmony_ci
15118c2ecf20Sopenharmony_ci	/* Write the divisor */
15128c2ecf20Sopenharmony_ci	write_mos_reg(serial, number, MOS7720_DLL, (__u8)(divisor & 0xff));
15138c2ecf20Sopenharmony_ci	write_mos_reg(serial, number, MOS7720_DLM,
15148c2ecf20Sopenharmony_ci		      (__u8)((divisor & 0xff00) >> 8));
15158c2ecf20Sopenharmony_ci
15168c2ecf20Sopenharmony_ci	/* Disable access to divisor latch */
15178c2ecf20Sopenharmony_ci	mos7720_port->shadowLCR = mos7720_port->shadowLCR & ~UART_LCR_DLAB;
15188c2ecf20Sopenharmony_ci	write_mos_reg(serial, number, MOS7720_LCR, mos7720_port->shadowLCR);
15198c2ecf20Sopenharmony_ci
15208c2ecf20Sopenharmony_ci	return status;
15218c2ecf20Sopenharmony_ci}
15228c2ecf20Sopenharmony_ci
15238c2ecf20Sopenharmony_ci/*
15248c2ecf20Sopenharmony_ci * change_port_settings
15258c2ecf20Sopenharmony_ci *	This routine is called to set the UART on the device to match
15268c2ecf20Sopenharmony_ci *      the specified new settings.
15278c2ecf20Sopenharmony_ci */
15288c2ecf20Sopenharmony_cistatic void change_port_settings(struct tty_struct *tty,
15298c2ecf20Sopenharmony_ci				 struct moschip_port *mos7720_port,
15308c2ecf20Sopenharmony_ci				 struct ktermios *old_termios)
15318c2ecf20Sopenharmony_ci{
15328c2ecf20Sopenharmony_ci	struct usb_serial_port *port;
15338c2ecf20Sopenharmony_ci	struct usb_serial *serial;
15348c2ecf20Sopenharmony_ci	int baud;
15358c2ecf20Sopenharmony_ci	unsigned cflag;
15368c2ecf20Sopenharmony_ci	__u8 lData;
15378c2ecf20Sopenharmony_ci	__u8 lParity;
15388c2ecf20Sopenharmony_ci	__u8 lStop;
15398c2ecf20Sopenharmony_ci	int status;
15408c2ecf20Sopenharmony_ci	int port_number;
15418c2ecf20Sopenharmony_ci
15428c2ecf20Sopenharmony_ci	if (mos7720_port == NULL)
15438c2ecf20Sopenharmony_ci		return ;
15448c2ecf20Sopenharmony_ci
15458c2ecf20Sopenharmony_ci	port = mos7720_port->port;
15468c2ecf20Sopenharmony_ci	serial = port->serial;
15478c2ecf20Sopenharmony_ci	port_number = port->port_number;
15488c2ecf20Sopenharmony_ci
15498c2ecf20Sopenharmony_ci	if (!mos7720_port->open) {
15508c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - port not opened\n", __func__);
15518c2ecf20Sopenharmony_ci		return;
15528c2ecf20Sopenharmony_ci	}
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_ci	lData = UART_LCR_WLEN8;
15558c2ecf20Sopenharmony_ci	lStop = 0x00;	/* 1 stop bit */
15568c2ecf20Sopenharmony_ci	lParity = 0x00;	/* No parity */
15578c2ecf20Sopenharmony_ci
15588c2ecf20Sopenharmony_ci	cflag = tty->termios.c_cflag;
15598c2ecf20Sopenharmony_ci
15608c2ecf20Sopenharmony_ci	/* Change the number of bits */
15618c2ecf20Sopenharmony_ci	switch (cflag & CSIZE) {
15628c2ecf20Sopenharmony_ci	case CS5:
15638c2ecf20Sopenharmony_ci		lData = UART_LCR_WLEN5;
15648c2ecf20Sopenharmony_ci		break;
15658c2ecf20Sopenharmony_ci
15668c2ecf20Sopenharmony_ci	case CS6:
15678c2ecf20Sopenharmony_ci		lData = UART_LCR_WLEN6;
15688c2ecf20Sopenharmony_ci		break;
15698c2ecf20Sopenharmony_ci
15708c2ecf20Sopenharmony_ci	case CS7:
15718c2ecf20Sopenharmony_ci		lData = UART_LCR_WLEN7;
15728c2ecf20Sopenharmony_ci		break;
15738c2ecf20Sopenharmony_ci	default:
15748c2ecf20Sopenharmony_ci	case CS8:
15758c2ecf20Sopenharmony_ci		lData = UART_LCR_WLEN8;
15768c2ecf20Sopenharmony_ci		break;
15778c2ecf20Sopenharmony_ci	}
15788c2ecf20Sopenharmony_ci
15798c2ecf20Sopenharmony_ci	/* Change the Parity bit */
15808c2ecf20Sopenharmony_ci	if (cflag & PARENB) {
15818c2ecf20Sopenharmony_ci		if (cflag & PARODD) {
15828c2ecf20Sopenharmony_ci			lParity = UART_LCR_PARITY;
15838c2ecf20Sopenharmony_ci			dev_dbg(&port->dev, "%s - parity = odd\n", __func__);
15848c2ecf20Sopenharmony_ci		} else {
15858c2ecf20Sopenharmony_ci			lParity = (UART_LCR_EPAR | UART_LCR_PARITY);
15868c2ecf20Sopenharmony_ci			dev_dbg(&port->dev, "%s - parity = even\n", __func__);
15878c2ecf20Sopenharmony_ci		}
15888c2ecf20Sopenharmony_ci
15898c2ecf20Sopenharmony_ci	} else {
15908c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - parity = none\n", __func__);
15918c2ecf20Sopenharmony_ci	}
15928c2ecf20Sopenharmony_ci
15938c2ecf20Sopenharmony_ci	if (cflag & CMSPAR)
15948c2ecf20Sopenharmony_ci		lParity = lParity | 0x20;
15958c2ecf20Sopenharmony_ci
15968c2ecf20Sopenharmony_ci	/* Change the Stop bit */
15978c2ecf20Sopenharmony_ci	if (cflag & CSTOPB) {
15988c2ecf20Sopenharmony_ci		lStop = UART_LCR_STOP;
15998c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - stop bits = 2\n", __func__);
16008c2ecf20Sopenharmony_ci	} else {
16018c2ecf20Sopenharmony_ci		lStop = 0x00;
16028c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - stop bits = 1\n", __func__);
16038c2ecf20Sopenharmony_ci	}
16048c2ecf20Sopenharmony_ci
16058c2ecf20Sopenharmony_ci#define LCR_BITS_MASK		0x03	/* Mask for bits/char field */
16068c2ecf20Sopenharmony_ci#define LCR_STOP_MASK		0x04	/* Mask for stop bits field */
16078c2ecf20Sopenharmony_ci#define LCR_PAR_MASK		0x38	/* Mask for parity field */
16088c2ecf20Sopenharmony_ci
16098c2ecf20Sopenharmony_ci	/* Update the LCR with the correct value */
16108c2ecf20Sopenharmony_ci	mos7720_port->shadowLCR &=
16118c2ecf20Sopenharmony_ci		~(LCR_BITS_MASK | LCR_STOP_MASK | LCR_PAR_MASK);
16128c2ecf20Sopenharmony_ci	mos7720_port->shadowLCR |= (lData | lParity | lStop);
16138c2ecf20Sopenharmony_ci
16148c2ecf20Sopenharmony_ci
16158c2ecf20Sopenharmony_ci	/* Disable Interrupts */
16168c2ecf20Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_IER, 0x00);
16178c2ecf20Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_FCR, 0x00);
16188c2ecf20Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_FCR, 0xcf);
16198c2ecf20Sopenharmony_ci
16208c2ecf20Sopenharmony_ci	/* Send the updated LCR value to the mos7720 */
16218c2ecf20Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_LCR,
16228c2ecf20Sopenharmony_ci		      mos7720_port->shadowLCR);
16238c2ecf20Sopenharmony_ci	mos7720_port->shadowMCR = 0x0b;
16248c2ecf20Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_MCR,
16258c2ecf20Sopenharmony_ci		      mos7720_port->shadowMCR);
16268c2ecf20Sopenharmony_ci
16278c2ecf20Sopenharmony_ci	/* set up the MCR register and send it to the mos7720 */
16288c2ecf20Sopenharmony_ci	mos7720_port->shadowMCR = UART_MCR_OUT2;
16298c2ecf20Sopenharmony_ci	if (cflag & CBAUD)
16308c2ecf20Sopenharmony_ci		mos7720_port->shadowMCR |= (UART_MCR_DTR | UART_MCR_RTS);
16318c2ecf20Sopenharmony_ci
16328c2ecf20Sopenharmony_ci	if (cflag & CRTSCTS) {
16338c2ecf20Sopenharmony_ci		mos7720_port->shadowMCR |= (UART_MCR_XONANY);
16348c2ecf20Sopenharmony_ci		/* To set hardware flow control to the specified *
16358c2ecf20Sopenharmony_ci		 * serial port, in SP1/2_CONTROL_REG             */
16368c2ecf20Sopenharmony_ci		if (port_number)
16378c2ecf20Sopenharmony_ci			write_mos_reg(serial, dummy, MOS7720_SP_CONTROL_REG,
16388c2ecf20Sopenharmony_ci				      0x01);
16398c2ecf20Sopenharmony_ci		else
16408c2ecf20Sopenharmony_ci			write_mos_reg(serial, dummy, MOS7720_SP_CONTROL_REG,
16418c2ecf20Sopenharmony_ci				      0x02);
16428c2ecf20Sopenharmony_ci
16438c2ecf20Sopenharmony_ci	} else
16448c2ecf20Sopenharmony_ci		mos7720_port->shadowMCR &= ~(UART_MCR_XONANY);
16458c2ecf20Sopenharmony_ci
16468c2ecf20Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_MCR,
16478c2ecf20Sopenharmony_ci		      mos7720_port->shadowMCR);
16488c2ecf20Sopenharmony_ci
16498c2ecf20Sopenharmony_ci	/* Determine divisor based on baud rate */
16508c2ecf20Sopenharmony_ci	baud = tty_get_baud_rate(tty);
16518c2ecf20Sopenharmony_ci	if (!baud) {
16528c2ecf20Sopenharmony_ci		/* pick a default, any default... */
16538c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "Picked default baud...\n");
16548c2ecf20Sopenharmony_ci		baud = 9600;
16558c2ecf20Sopenharmony_ci	}
16568c2ecf20Sopenharmony_ci
16578c2ecf20Sopenharmony_ci	if (baud >= 230400) {
16588c2ecf20Sopenharmony_ci		set_higher_rates(mos7720_port, baud);
16598c2ecf20Sopenharmony_ci		/* Enable Interrupts */
16608c2ecf20Sopenharmony_ci		write_mos_reg(serial, port_number, MOS7720_IER, 0x0c);
16618c2ecf20Sopenharmony_ci		return;
16628c2ecf20Sopenharmony_ci	}
16638c2ecf20Sopenharmony_ci
16648c2ecf20Sopenharmony_ci	dev_dbg(&port->dev, "%s - baud rate = %d\n", __func__, baud);
16658c2ecf20Sopenharmony_ci	status = send_cmd_write_baud_rate(mos7720_port, baud);
16668c2ecf20Sopenharmony_ci	/* FIXME: needs to write actual resulting baud back not just
16678c2ecf20Sopenharmony_ci	   blindly do so */
16688c2ecf20Sopenharmony_ci	if (cflag & CBAUD)
16698c2ecf20Sopenharmony_ci		tty_encode_baud_rate(tty, baud, baud);
16708c2ecf20Sopenharmony_ci	/* Enable Interrupts */
16718c2ecf20Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_IER, 0x0c);
16728c2ecf20Sopenharmony_ci
16738c2ecf20Sopenharmony_ci	if (port->read_urb->status != -EINPROGRESS) {
16748c2ecf20Sopenharmony_ci		status = usb_submit_urb(port->read_urb, GFP_KERNEL);
16758c2ecf20Sopenharmony_ci		if (status)
16768c2ecf20Sopenharmony_ci			dev_dbg(&port->dev, "usb_submit_urb(read bulk) failed, status = %d\n", status);
16778c2ecf20Sopenharmony_ci	}
16788c2ecf20Sopenharmony_ci}
16798c2ecf20Sopenharmony_ci
16808c2ecf20Sopenharmony_ci/*
16818c2ecf20Sopenharmony_ci * mos7720_set_termios
16828c2ecf20Sopenharmony_ci *	this function is called by the tty driver when it wants to change the
16838c2ecf20Sopenharmony_ci *	termios structure.
16848c2ecf20Sopenharmony_ci */
16858c2ecf20Sopenharmony_cistatic void mos7720_set_termios(struct tty_struct *tty,
16868c2ecf20Sopenharmony_ci		struct usb_serial_port *port, struct ktermios *old_termios)
16878c2ecf20Sopenharmony_ci{
16888c2ecf20Sopenharmony_ci	int status;
16898c2ecf20Sopenharmony_ci	struct moschip_port *mos7720_port;
16908c2ecf20Sopenharmony_ci
16918c2ecf20Sopenharmony_ci	mos7720_port = usb_get_serial_port_data(port);
16928c2ecf20Sopenharmony_ci
16938c2ecf20Sopenharmony_ci	if (mos7720_port == NULL)
16948c2ecf20Sopenharmony_ci		return;
16958c2ecf20Sopenharmony_ci
16968c2ecf20Sopenharmony_ci	if (!mos7720_port->open) {
16978c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - port not opened\n", __func__);
16988c2ecf20Sopenharmony_ci		return;
16998c2ecf20Sopenharmony_ci	}
17008c2ecf20Sopenharmony_ci
17018c2ecf20Sopenharmony_ci	/* change the port settings to the new ones specified */
17028c2ecf20Sopenharmony_ci	change_port_settings(tty, mos7720_port, old_termios);
17038c2ecf20Sopenharmony_ci
17048c2ecf20Sopenharmony_ci	if (port->read_urb->status != -EINPROGRESS) {
17058c2ecf20Sopenharmony_ci		status = usb_submit_urb(port->read_urb, GFP_KERNEL);
17068c2ecf20Sopenharmony_ci		if (status)
17078c2ecf20Sopenharmony_ci			dev_dbg(&port->dev, "usb_submit_urb(read bulk) failed, status = %d\n", status);
17088c2ecf20Sopenharmony_ci	}
17098c2ecf20Sopenharmony_ci}
17108c2ecf20Sopenharmony_ci
17118c2ecf20Sopenharmony_ci/*
17128c2ecf20Sopenharmony_ci * get_lsr_info - get line status register info
17138c2ecf20Sopenharmony_ci *
17148c2ecf20Sopenharmony_ci * Purpose: Let user call ioctl() to get info when the UART physically
17158c2ecf20Sopenharmony_ci * 	    is emptied.  On bus types like RS485, the transmitter must
17168c2ecf20Sopenharmony_ci * 	    release the bus after transmitting. This must be done when
17178c2ecf20Sopenharmony_ci * 	    the transmit shift register is empty, not be done when the
17188c2ecf20Sopenharmony_ci * 	    transmit holding register is empty.  This functionality
17198c2ecf20Sopenharmony_ci * 	    allows an RS485 driver to be written in user space.
17208c2ecf20Sopenharmony_ci */
17218c2ecf20Sopenharmony_cistatic int get_lsr_info(struct tty_struct *tty,
17228c2ecf20Sopenharmony_ci		struct moschip_port *mos7720_port, unsigned int __user *value)
17238c2ecf20Sopenharmony_ci{
17248c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
17258c2ecf20Sopenharmony_ci	unsigned int result = 0;
17268c2ecf20Sopenharmony_ci	unsigned char data = 0;
17278c2ecf20Sopenharmony_ci	int port_number = port->port_number;
17288c2ecf20Sopenharmony_ci	int count;
17298c2ecf20Sopenharmony_ci
17308c2ecf20Sopenharmony_ci	count = mos7720_chars_in_buffer(tty);
17318c2ecf20Sopenharmony_ci	if (count == 0) {
17328c2ecf20Sopenharmony_ci		read_mos_reg(port->serial, port_number, MOS7720_LSR, &data);
17338c2ecf20Sopenharmony_ci		if ((data & (UART_LSR_TEMT | UART_LSR_THRE))
17348c2ecf20Sopenharmony_ci					== (UART_LSR_TEMT | UART_LSR_THRE)) {
17358c2ecf20Sopenharmony_ci			dev_dbg(&port->dev, "%s -- Empty\n", __func__);
17368c2ecf20Sopenharmony_ci			result = TIOCSER_TEMT;
17378c2ecf20Sopenharmony_ci		}
17388c2ecf20Sopenharmony_ci	}
17398c2ecf20Sopenharmony_ci	if (copy_to_user(value, &result, sizeof(int)))
17408c2ecf20Sopenharmony_ci		return -EFAULT;
17418c2ecf20Sopenharmony_ci	return 0;
17428c2ecf20Sopenharmony_ci}
17438c2ecf20Sopenharmony_ci
17448c2ecf20Sopenharmony_cistatic int mos7720_tiocmget(struct tty_struct *tty)
17458c2ecf20Sopenharmony_ci{
17468c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
17478c2ecf20Sopenharmony_ci	struct moschip_port *mos7720_port = usb_get_serial_port_data(port);
17488c2ecf20Sopenharmony_ci	unsigned int result = 0;
17498c2ecf20Sopenharmony_ci	unsigned int mcr ;
17508c2ecf20Sopenharmony_ci	unsigned int msr ;
17518c2ecf20Sopenharmony_ci
17528c2ecf20Sopenharmony_ci	mcr = mos7720_port->shadowMCR;
17538c2ecf20Sopenharmony_ci	msr = mos7720_port->shadowMSR;
17548c2ecf20Sopenharmony_ci
17558c2ecf20Sopenharmony_ci	result = ((mcr & UART_MCR_DTR)  ? TIOCM_DTR : 0)   /* 0x002 */
17568c2ecf20Sopenharmony_ci	  | ((mcr & UART_MCR_RTS)   ? TIOCM_RTS : 0)   /* 0x004 */
17578c2ecf20Sopenharmony_ci	  | ((msr & UART_MSR_CTS)   ? TIOCM_CTS : 0)   /* 0x020 */
17588c2ecf20Sopenharmony_ci	  | ((msr & UART_MSR_DCD)   ? TIOCM_CAR : 0)   /* 0x040 */
17598c2ecf20Sopenharmony_ci	  | ((msr & UART_MSR_RI)    ? TIOCM_RI :  0)   /* 0x080 */
17608c2ecf20Sopenharmony_ci	  | ((msr & UART_MSR_DSR)   ? TIOCM_DSR : 0);  /* 0x100 */
17618c2ecf20Sopenharmony_ci
17628c2ecf20Sopenharmony_ci	return result;
17638c2ecf20Sopenharmony_ci}
17648c2ecf20Sopenharmony_ci
17658c2ecf20Sopenharmony_cistatic int mos7720_tiocmset(struct tty_struct *tty,
17668c2ecf20Sopenharmony_ci			    unsigned int set, unsigned int clear)
17678c2ecf20Sopenharmony_ci{
17688c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
17698c2ecf20Sopenharmony_ci	struct moschip_port *mos7720_port = usb_get_serial_port_data(port);
17708c2ecf20Sopenharmony_ci	unsigned int mcr ;
17718c2ecf20Sopenharmony_ci
17728c2ecf20Sopenharmony_ci	mcr = mos7720_port->shadowMCR;
17738c2ecf20Sopenharmony_ci
17748c2ecf20Sopenharmony_ci	if (set & TIOCM_RTS)
17758c2ecf20Sopenharmony_ci		mcr |= UART_MCR_RTS;
17768c2ecf20Sopenharmony_ci	if (set & TIOCM_DTR)
17778c2ecf20Sopenharmony_ci		mcr |= UART_MCR_DTR;
17788c2ecf20Sopenharmony_ci	if (set & TIOCM_LOOP)
17798c2ecf20Sopenharmony_ci		mcr |= UART_MCR_LOOP;
17808c2ecf20Sopenharmony_ci
17818c2ecf20Sopenharmony_ci	if (clear & TIOCM_RTS)
17828c2ecf20Sopenharmony_ci		mcr &= ~UART_MCR_RTS;
17838c2ecf20Sopenharmony_ci	if (clear & TIOCM_DTR)
17848c2ecf20Sopenharmony_ci		mcr &= ~UART_MCR_DTR;
17858c2ecf20Sopenharmony_ci	if (clear & TIOCM_LOOP)
17868c2ecf20Sopenharmony_ci		mcr &= ~UART_MCR_LOOP;
17878c2ecf20Sopenharmony_ci
17888c2ecf20Sopenharmony_ci	mos7720_port->shadowMCR = mcr;
17898c2ecf20Sopenharmony_ci	write_mos_reg(port->serial, port->port_number, MOS7720_MCR,
17908c2ecf20Sopenharmony_ci		      mos7720_port->shadowMCR);
17918c2ecf20Sopenharmony_ci
17928c2ecf20Sopenharmony_ci	return 0;
17938c2ecf20Sopenharmony_ci}
17948c2ecf20Sopenharmony_ci
17958c2ecf20Sopenharmony_cistatic int get_serial_info(struct tty_struct *tty,
17968c2ecf20Sopenharmony_ci			   struct serial_struct *ss)
17978c2ecf20Sopenharmony_ci{
17988c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
17998c2ecf20Sopenharmony_ci	struct moschip_port *mos7720_port = usb_get_serial_port_data(port);
18008c2ecf20Sopenharmony_ci
18018c2ecf20Sopenharmony_ci	ss->type		= PORT_16550A;
18028c2ecf20Sopenharmony_ci	ss->line		= mos7720_port->port->minor;
18038c2ecf20Sopenharmony_ci	ss->port		= mos7720_port->port->port_number;
18048c2ecf20Sopenharmony_ci	ss->irq			= 0;
18058c2ecf20Sopenharmony_ci	ss->xmit_fifo_size	= NUM_URBS * URB_TRANSFER_BUFFER_SIZE;
18068c2ecf20Sopenharmony_ci	ss->baud_base		= 9600;
18078c2ecf20Sopenharmony_ci	ss->close_delay		= 5*HZ;
18088c2ecf20Sopenharmony_ci	ss->closing_wait	= 30*HZ;
18098c2ecf20Sopenharmony_ci	return 0;
18108c2ecf20Sopenharmony_ci}
18118c2ecf20Sopenharmony_ci
18128c2ecf20Sopenharmony_cistatic int mos7720_ioctl(struct tty_struct *tty,
18138c2ecf20Sopenharmony_ci			 unsigned int cmd, unsigned long arg)
18148c2ecf20Sopenharmony_ci{
18158c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
18168c2ecf20Sopenharmony_ci	struct moschip_port *mos7720_port;
18178c2ecf20Sopenharmony_ci
18188c2ecf20Sopenharmony_ci	mos7720_port = usb_get_serial_port_data(port);
18198c2ecf20Sopenharmony_ci	if (mos7720_port == NULL)
18208c2ecf20Sopenharmony_ci		return -ENODEV;
18218c2ecf20Sopenharmony_ci
18228c2ecf20Sopenharmony_ci	switch (cmd) {
18238c2ecf20Sopenharmony_ci	case TIOCSERGETLSR:
18248c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s TIOCSERGETLSR\n", __func__);
18258c2ecf20Sopenharmony_ci		return get_lsr_info(tty, mos7720_port,
18268c2ecf20Sopenharmony_ci					(unsigned int __user *)arg);
18278c2ecf20Sopenharmony_ci	}
18288c2ecf20Sopenharmony_ci
18298c2ecf20Sopenharmony_ci	return -ENOIOCTLCMD;
18308c2ecf20Sopenharmony_ci}
18318c2ecf20Sopenharmony_ci
18328c2ecf20Sopenharmony_cistatic int mos7720_startup(struct usb_serial *serial)
18338c2ecf20Sopenharmony_ci{
18348c2ecf20Sopenharmony_ci	struct usb_device *dev;
18358c2ecf20Sopenharmony_ci	char data;
18368c2ecf20Sopenharmony_ci	u16 product;
18378c2ecf20Sopenharmony_ci	int ret_val;
18388c2ecf20Sopenharmony_ci
18398c2ecf20Sopenharmony_ci	product = le16_to_cpu(serial->dev->descriptor.idProduct);
18408c2ecf20Sopenharmony_ci	dev = serial->dev;
18418c2ecf20Sopenharmony_ci
18428c2ecf20Sopenharmony_ci	if (product == MOSCHIP_DEVICE_ID_7715) {
18438c2ecf20Sopenharmony_ci		struct urb *urb = serial->port[0]->interrupt_in_urb;
18448c2ecf20Sopenharmony_ci
18458c2ecf20Sopenharmony_ci		urb->complete = mos7715_interrupt_callback;
18468c2ecf20Sopenharmony_ci
18478c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
18488c2ecf20Sopenharmony_ci		ret_val = mos7715_parport_init(serial);
18498c2ecf20Sopenharmony_ci		if (ret_val < 0)
18508c2ecf20Sopenharmony_ci			return ret_val;
18518c2ecf20Sopenharmony_ci#endif
18528c2ecf20Sopenharmony_ci	}
18538c2ecf20Sopenharmony_ci	/* start the interrupt urb */
18548c2ecf20Sopenharmony_ci	ret_val = usb_submit_urb(serial->port[0]->interrupt_in_urb, GFP_KERNEL);
18558c2ecf20Sopenharmony_ci	if (ret_val) {
18568c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "failed to submit interrupt urb: %d\n",
18578c2ecf20Sopenharmony_ci			ret_val);
18588c2ecf20Sopenharmony_ci	}
18598c2ecf20Sopenharmony_ci
18608c2ecf20Sopenharmony_ci	/* LSR For Port 1 */
18618c2ecf20Sopenharmony_ci	read_mos_reg(serial, 0, MOS7720_LSR, &data);
18628c2ecf20Sopenharmony_ci	dev_dbg(&dev->dev, "LSR:%x\n", data);
18638c2ecf20Sopenharmony_ci
18648c2ecf20Sopenharmony_ci	return 0;
18658c2ecf20Sopenharmony_ci}
18668c2ecf20Sopenharmony_ci
18678c2ecf20Sopenharmony_cistatic void mos7720_release(struct usb_serial *serial)
18688c2ecf20Sopenharmony_ci{
18698c2ecf20Sopenharmony_ci	usb_kill_urb(serial->port[0]->interrupt_in_urb);
18708c2ecf20Sopenharmony_ci
18718c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
18728c2ecf20Sopenharmony_ci	/* close the parallel port */
18738c2ecf20Sopenharmony_ci
18748c2ecf20Sopenharmony_ci	if (le16_to_cpu(serial->dev->descriptor.idProduct)
18758c2ecf20Sopenharmony_ci	    == MOSCHIP_DEVICE_ID_7715) {
18768c2ecf20Sopenharmony_ci		struct urbtracker *urbtrack;
18778c2ecf20Sopenharmony_ci		unsigned long flags;
18788c2ecf20Sopenharmony_ci		struct mos7715_parport *mos_parport =
18798c2ecf20Sopenharmony_ci			usb_get_serial_data(serial);
18808c2ecf20Sopenharmony_ci
18818c2ecf20Sopenharmony_ci		/* prevent NULL ptr dereference in port callbacks */
18828c2ecf20Sopenharmony_ci		spin_lock(&release_lock);
18838c2ecf20Sopenharmony_ci		mos_parport->pp->private_data = NULL;
18848c2ecf20Sopenharmony_ci		spin_unlock(&release_lock);
18858c2ecf20Sopenharmony_ci
18868c2ecf20Sopenharmony_ci		/* wait for synchronous usb calls to return */
18878c2ecf20Sopenharmony_ci		if (mos_parport->msg_pending)
18888c2ecf20Sopenharmony_ci			wait_for_completion_timeout(&mos_parport->syncmsg_compl,
18898c2ecf20Sopenharmony_ci					    msecs_to_jiffies(MOS_WDR_TIMEOUT));
18908c2ecf20Sopenharmony_ci
18918c2ecf20Sopenharmony_ci		parport_remove_port(mos_parport->pp);
18928c2ecf20Sopenharmony_ci		usb_set_serial_data(serial, NULL);
18938c2ecf20Sopenharmony_ci		mos_parport->serial = NULL;
18948c2ecf20Sopenharmony_ci
18958c2ecf20Sopenharmony_ci		/* if tasklet currently scheduled, wait for it to complete */
18968c2ecf20Sopenharmony_ci		tasklet_kill(&mos_parport->urb_tasklet);
18978c2ecf20Sopenharmony_ci
18988c2ecf20Sopenharmony_ci		/* unlink any urbs sent by the tasklet  */
18998c2ecf20Sopenharmony_ci		spin_lock_irqsave(&mos_parport->listlock, flags);
19008c2ecf20Sopenharmony_ci		list_for_each_entry(urbtrack,
19018c2ecf20Sopenharmony_ci				    &mos_parport->active_urbs,
19028c2ecf20Sopenharmony_ci				    urblist_entry)
19038c2ecf20Sopenharmony_ci			usb_unlink_urb(urbtrack->urb);
19048c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&mos_parport->listlock, flags);
19058c2ecf20Sopenharmony_ci		parport_del_port(mos_parport->pp);
19068c2ecf20Sopenharmony_ci
19078c2ecf20Sopenharmony_ci		kref_put(&mos_parport->ref_count, destroy_mos_parport);
19088c2ecf20Sopenharmony_ci	}
19098c2ecf20Sopenharmony_ci#endif
19108c2ecf20Sopenharmony_ci}
19118c2ecf20Sopenharmony_ci
19128c2ecf20Sopenharmony_cistatic int mos7720_port_probe(struct usb_serial_port *port)
19138c2ecf20Sopenharmony_ci{
19148c2ecf20Sopenharmony_ci	struct moschip_port *mos7720_port;
19158c2ecf20Sopenharmony_ci
19168c2ecf20Sopenharmony_ci	mos7720_port = kzalloc(sizeof(*mos7720_port), GFP_KERNEL);
19178c2ecf20Sopenharmony_ci	if (!mos7720_port)
19188c2ecf20Sopenharmony_ci		return -ENOMEM;
19198c2ecf20Sopenharmony_ci
19208c2ecf20Sopenharmony_ci	mos7720_port->port = port;
19218c2ecf20Sopenharmony_ci
19228c2ecf20Sopenharmony_ci	usb_set_serial_port_data(port, mos7720_port);
19238c2ecf20Sopenharmony_ci
19248c2ecf20Sopenharmony_ci	return 0;
19258c2ecf20Sopenharmony_ci}
19268c2ecf20Sopenharmony_ci
19278c2ecf20Sopenharmony_cistatic int mos7720_port_remove(struct usb_serial_port *port)
19288c2ecf20Sopenharmony_ci{
19298c2ecf20Sopenharmony_ci	struct moschip_port *mos7720_port;
19308c2ecf20Sopenharmony_ci
19318c2ecf20Sopenharmony_ci	mos7720_port = usb_get_serial_port_data(port);
19328c2ecf20Sopenharmony_ci	kfree(mos7720_port);
19338c2ecf20Sopenharmony_ci
19348c2ecf20Sopenharmony_ci	return 0;
19358c2ecf20Sopenharmony_ci}
19368c2ecf20Sopenharmony_ci
19378c2ecf20Sopenharmony_cistatic struct usb_serial_driver moschip7720_2port_driver = {
19388c2ecf20Sopenharmony_ci	.driver = {
19398c2ecf20Sopenharmony_ci		.owner =	THIS_MODULE,
19408c2ecf20Sopenharmony_ci		.name =		"moschip7720",
19418c2ecf20Sopenharmony_ci	},
19428c2ecf20Sopenharmony_ci	.description		= "Moschip 2 port adapter",
19438c2ecf20Sopenharmony_ci	.id_table		= id_table,
19448c2ecf20Sopenharmony_ci	.num_bulk_in		= 2,
19458c2ecf20Sopenharmony_ci	.num_bulk_out		= 2,
19468c2ecf20Sopenharmony_ci	.num_interrupt_in	= 1,
19478c2ecf20Sopenharmony_ci	.calc_num_ports		= mos77xx_calc_num_ports,
19488c2ecf20Sopenharmony_ci	.open			= mos7720_open,
19498c2ecf20Sopenharmony_ci	.close			= mos7720_close,
19508c2ecf20Sopenharmony_ci	.throttle		= mos7720_throttle,
19518c2ecf20Sopenharmony_ci	.unthrottle		= mos7720_unthrottle,
19528c2ecf20Sopenharmony_ci	.attach			= mos7720_startup,
19538c2ecf20Sopenharmony_ci	.release		= mos7720_release,
19548c2ecf20Sopenharmony_ci	.port_probe		= mos7720_port_probe,
19558c2ecf20Sopenharmony_ci	.port_remove		= mos7720_port_remove,
19568c2ecf20Sopenharmony_ci	.ioctl			= mos7720_ioctl,
19578c2ecf20Sopenharmony_ci	.tiocmget		= mos7720_tiocmget,
19588c2ecf20Sopenharmony_ci	.tiocmset		= mos7720_tiocmset,
19598c2ecf20Sopenharmony_ci	.get_serial		= get_serial_info,
19608c2ecf20Sopenharmony_ci	.set_termios		= mos7720_set_termios,
19618c2ecf20Sopenharmony_ci	.write			= mos7720_write,
19628c2ecf20Sopenharmony_ci	.write_room		= mos7720_write_room,
19638c2ecf20Sopenharmony_ci	.chars_in_buffer	= mos7720_chars_in_buffer,
19648c2ecf20Sopenharmony_ci	.break_ctl		= mos7720_break,
19658c2ecf20Sopenharmony_ci	.read_bulk_callback	= mos7720_bulk_in_callback,
19668c2ecf20Sopenharmony_ci	.read_int_callback	= mos7720_interrupt_callback,
19678c2ecf20Sopenharmony_ci};
19688c2ecf20Sopenharmony_ci
19698c2ecf20Sopenharmony_cistatic struct usb_serial_driver * const serial_drivers[] = {
19708c2ecf20Sopenharmony_ci	&moschip7720_2port_driver, NULL
19718c2ecf20Sopenharmony_ci};
19728c2ecf20Sopenharmony_ci
19738c2ecf20Sopenharmony_cimodule_usb_serial_driver(serial_drivers, id_table);
19748c2ecf20Sopenharmony_ci
19758c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR);
19768c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
19778c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
1978