18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Fintek F81232 USB to serial adaptor driver
48c2ecf20Sopenharmony_ci * Fintek F81532A/534A/535/536 USB to 2/4/8/12 serial adaptor driver
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (C) 2012 Greg Kroah-Hartman (gregkh@linuxfoundation.org)
78c2ecf20Sopenharmony_ci * Copyright (C) 2012 Linux Foundation
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/errno.h>
128c2ecf20Sopenharmony_ci#include <linux/slab.h>
138c2ecf20Sopenharmony_ci#include <linux/tty.h>
148c2ecf20Sopenharmony_ci#include <linux/tty_driver.h>
158c2ecf20Sopenharmony_ci#include <linux/tty_flip.h>
168c2ecf20Sopenharmony_ci#include <linux/serial.h>
178c2ecf20Sopenharmony_ci#include <linux/module.h>
188c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
198c2ecf20Sopenharmony_ci#include <linux/mutex.h>
208c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
218c2ecf20Sopenharmony_ci#include <linux/usb.h>
228c2ecf20Sopenharmony_ci#include <linux/usb/serial.h>
238c2ecf20Sopenharmony_ci#include <linux/serial_reg.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define F81232_ID		\
268c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x1934, 0x0706) }	/* 1 port UART device */
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#define F81534A_SERIES_ID	\
298c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x2c42, 0x1602) },	/* In-Box 2 port UART device */	\
308c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x2c42, 0x1604) },	/* In-Box 4 port UART device */	\
318c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x2c42, 0x1605) },	/* In-Box 8 port UART device */	\
328c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x2c42, 0x1606) },	/* In-Box 12 port UART device */ \
338c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x2c42, 0x1608) },	/* Non-Flash type */ \
348c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x2c42, 0x1632) },	/* 2 port UART device */ \
358c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x2c42, 0x1634) },	/* 4 port UART device */ \
368c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x2c42, 0x1635) },	/* 8 port UART device */ \
378c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x2c42, 0x1636) }	/* 12 port UART device */
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#define F81534A_CTRL_ID		\
408c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x2c42, 0x16f8) }	/* Global control device */
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic const struct usb_device_id f81232_id_table[] = {
438c2ecf20Sopenharmony_ci	F81232_ID,
448c2ecf20Sopenharmony_ci	{ }					/* Terminating entry */
458c2ecf20Sopenharmony_ci};
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic const struct usb_device_id f81534a_id_table[] = {
488c2ecf20Sopenharmony_ci	F81534A_SERIES_ID,
498c2ecf20Sopenharmony_ci	{ }					/* Terminating entry */
508c2ecf20Sopenharmony_ci};
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic const struct usb_device_id f81534a_ctrl_id_table[] = {
538c2ecf20Sopenharmony_ci	F81534A_CTRL_ID,
548c2ecf20Sopenharmony_ci	{ }					/* Terminating entry */
558c2ecf20Sopenharmony_ci};
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic const struct usb_device_id combined_id_table[] = {
588c2ecf20Sopenharmony_ci	F81232_ID,
598c2ecf20Sopenharmony_ci	F81534A_SERIES_ID,
608c2ecf20Sopenharmony_ci	F81534A_CTRL_ID,
618c2ecf20Sopenharmony_ci	{ }					/* Terminating entry */
628c2ecf20Sopenharmony_ci};
638c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, combined_id_table);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci/* Maximum baudrate for F81232 */
668c2ecf20Sopenharmony_ci#define F81232_MAX_BAUDRATE		1500000
678c2ecf20Sopenharmony_ci#define F81232_DEF_BAUDRATE		9600
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci/* USB Control EP parameter */
708c2ecf20Sopenharmony_ci#define F81232_REGISTER_REQUEST		0xa0
718c2ecf20Sopenharmony_ci#define F81232_GET_REGISTER		0xc0
728c2ecf20Sopenharmony_ci#define F81232_SET_REGISTER		0x40
738c2ecf20Sopenharmony_ci#define F81534A_ACCESS_REG_RETRY	2
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci#define SERIAL_BASE_ADDRESS		0x0120
768c2ecf20Sopenharmony_ci#define RECEIVE_BUFFER_REGISTER		(0x00 + SERIAL_BASE_ADDRESS)
778c2ecf20Sopenharmony_ci#define INTERRUPT_ENABLE_REGISTER	(0x01 + SERIAL_BASE_ADDRESS)
788c2ecf20Sopenharmony_ci#define FIFO_CONTROL_REGISTER		(0x02 + SERIAL_BASE_ADDRESS)
798c2ecf20Sopenharmony_ci#define LINE_CONTROL_REGISTER		(0x03 + SERIAL_BASE_ADDRESS)
808c2ecf20Sopenharmony_ci#define MODEM_CONTROL_REGISTER		(0x04 + SERIAL_BASE_ADDRESS)
818c2ecf20Sopenharmony_ci#define LINE_STATUS_REGISTER		(0x05 + SERIAL_BASE_ADDRESS)
828c2ecf20Sopenharmony_ci#define MODEM_STATUS_REGISTER		(0x06 + SERIAL_BASE_ADDRESS)
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci/*
858c2ecf20Sopenharmony_ci * F81232 Clock registers (106h)
868c2ecf20Sopenharmony_ci *
878c2ecf20Sopenharmony_ci * Bit1-0:	Clock source selector
888c2ecf20Sopenharmony_ci *			00: 1.846MHz.
898c2ecf20Sopenharmony_ci *			01: 18.46MHz.
908c2ecf20Sopenharmony_ci *			10: 24MHz.
918c2ecf20Sopenharmony_ci *			11: 14.77MHz.
928c2ecf20Sopenharmony_ci */
938c2ecf20Sopenharmony_ci#define F81232_CLK_REGISTER		0x106
948c2ecf20Sopenharmony_ci#define F81232_CLK_1_846_MHZ		0
958c2ecf20Sopenharmony_ci#define F81232_CLK_18_46_MHZ		BIT(0)
968c2ecf20Sopenharmony_ci#define F81232_CLK_24_MHZ		BIT(1)
978c2ecf20Sopenharmony_ci#define F81232_CLK_14_77_MHZ		(BIT(1) | BIT(0))
988c2ecf20Sopenharmony_ci#define F81232_CLK_MASK			GENMASK(1, 0)
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci#define F81534A_MODE_REG		0x107
1018c2ecf20Sopenharmony_ci#define F81534A_TRIGGER_MASK		GENMASK(3, 2)
1028c2ecf20Sopenharmony_ci#define F81534A_TRIGGER_MULTIPLE_4X	BIT(3)
1038c2ecf20Sopenharmony_ci#define F81534A_FIFO_128BYTE		(BIT(1) | BIT(0))
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci/* Serial port self GPIO control, 2bytes [control&output data][input data] */
1068c2ecf20Sopenharmony_ci#define F81534A_GPIO_REG		0x10e
1078c2ecf20Sopenharmony_ci#define F81534A_GPIO_MODE2_DIR		BIT(6) /* 1: input, 0: output */
1088c2ecf20Sopenharmony_ci#define F81534A_GPIO_MODE1_DIR		BIT(5)
1098c2ecf20Sopenharmony_ci#define F81534A_GPIO_MODE0_DIR		BIT(4)
1108c2ecf20Sopenharmony_ci#define F81534A_GPIO_MODE2_OUTPUT	BIT(2)
1118c2ecf20Sopenharmony_ci#define F81534A_GPIO_MODE1_OUTPUT	BIT(1)
1128c2ecf20Sopenharmony_ci#define F81534A_GPIO_MODE0_OUTPUT	BIT(0)
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci#define F81534A_CTRL_CMD_ENABLE_PORT	0x116
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistruct f81232_private {
1178c2ecf20Sopenharmony_ci	struct mutex lock;
1188c2ecf20Sopenharmony_ci	u8 modem_control;
1198c2ecf20Sopenharmony_ci	u8 modem_status;
1208c2ecf20Sopenharmony_ci	u8 shadow_lcr;
1218c2ecf20Sopenharmony_ci	speed_t baud_base;
1228c2ecf20Sopenharmony_ci	struct work_struct lsr_work;
1238c2ecf20Sopenharmony_ci	struct work_struct interrupt_work;
1248c2ecf20Sopenharmony_ci	struct usb_serial_port *port;
1258c2ecf20Sopenharmony_ci};
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistatic u32 const baudrate_table[] = { 115200, 921600, 1152000, 1500000 };
1288c2ecf20Sopenharmony_cistatic u8 const clock_table[] = { F81232_CLK_1_846_MHZ, F81232_CLK_14_77_MHZ,
1298c2ecf20Sopenharmony_ci				F81232_CLK_18_46_MHZ, F81232_CLK_24_MHZ };
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic int calc_baud_divisor(speed_t baudrate, speed_t clockrate)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	return DIV_ROUND_CLOSEST(clockrate, baudrate);
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic int f81232_get_register(struct usb_serial_port *port, u16 reg, u8 *val)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	int status;
1398c2ecf20Sopenharmony_ci	u8 *tmp;
1408c2ecf20Sopenharmony_ci	struct usb_device *dev = port->serial->dev;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	tmp = kmalloc(sizeof(*val), GFP_KERNEL);
1438c2ecf20Sopenharmony_ci	if (!tmp)
1448c2ecf20Sopenharmony_ci		return -ENOMEM;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	status = usb_control_msg(dev,
1478c2ecf20Sopenharmony_ci				usb_rcvctrlpipe(dev, 0),
1488c2ecf20Sopenharmony_ci				F81232_REGISTER_REQUEST,
1498c2ecf20Sopenharmony_ci				F81232_GET_REGISTER,
1508c2ecf20Sopenharmony_ci				reg,
1518c2ecf20Sopenharmony_ci				0,
1528c2ecf20Sopenharmony_ci				tmp,
1538c2ecf20Sopenharmony_ci				sizeof(*val),
1548c2ecf20Sopenharmony_ci				USB_CTRL_GET_TIMEOUT);
1558c2ecf20Sopenharmony_ci	if (status != sizeof(*val)) {
1568c2ecf20Sopenharmony_ci		dev_err(&port->dev, "%s failed status: %d\n", __func__, status);
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci		if (status < 0)
1598c2ecf20Sopenharmony_ci			status = usb_translate_errors(status);
1608c2ecf20Sopenharmony_ci		else
1618c2ecf20Sopenharmony_ci			status = -EIO;
1628c2ecf20Sopenharmony_ci	} else {
1638c2ecf20Sopenharmony_ci		status = 0;
1648c2ecf20Sopenharmony_ci		*val = *tmp;
1658c2ecf20Sopenharmony_ci	}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	kfree(tmp);
1688c2ecf20Sopenharmony_ci	return status;
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_cistatic int f81232_set_register(struct usb_serial_port *port, u16 reg, u8 val)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	int status;
1748c2ecf20Sopenharmony_ci	u8 *tmp;
1758c2ecf20Sopenharmony_ci	struct usb_device *dev = port->serial->dev;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	tmp = kmalloc(sizeof(val), GFP_KERNEL);
1788c2ecf20Sopenharmony_ci	if (!tmp)
1798c2ecf20Sopenharmony_ci		return -ENOMEM;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	*tmp = val;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	status = usb_control_msg(dev,
1848c2ecf20Sopenharmony_ci				usb_sndctrlpipe(dev, 0),
1858c2ecf20Sopenharmony_ci				F81232_REGISTER_REQUEST,
1868c2ecf20Sopenharmony_ci				F81232_SET_REGISTER,
1878c2ecf20Sopenharmony_ci				reg,
1888c2ecf20Sopenharmony_ci				0,
1898c2ecf20Sopenharmony_ci				tmp,
1908c2ecf20Sopenharmony_ci				sizeof(val),
1918c2ecf20Sopenharmony_ci				USB_CTRL_SET_TIMEOUT);
1928c2ecf20Sopenharmony_ci	if (status != sizeof(val)) {
1938c2ecf20Sopenharmony_ci		dev_err(&port->dev, "%s failed status: %d\n", __func__, status);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci		if (status < 0)
1968c2ecf20Sopenharmony_ci			status = usb_translate_errors(status);
1978c2ecf20Sopenharmony_ci		else
1988c2ecf20Sopenharmony_ci			status = -EIO;
1998c2ecf20Sopenharmony_ci	} else {
2008c2ecf20Sopenharmony_ci		status = 0;
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	kfree(tmp);
2048c2ecf20Sopenharmony_ci	return status;
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cistatic int f81232_set_mask_register(struct usb_serial_port *port, u16 reg,
2088c2ecf20Sopenharmony_ci					u8 mask, u8 val)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	int status;
2118c2ecf20Sopenharmony_ci	u8 tmp;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	status = f81232_get_register(port, reg, &tmp);
2148c2ecf20Sopenharmony_ci	if (status)
2158c2ecf20Sopenharmony_ci		return status;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	tmp = (tmp & ~mask) | (val & mask);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	return f81232_set_register(port, reg, tmp);
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cistatic void f81232_read_msr(struct usb_serial_port *port)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci	int status;
2258c2ecf20Sopenharmony_ci	u8 current_msr;
2268c2ecf20Sopenharmony_ci	struct tty_struct *tty;
2278c2ecf20Sopenharmony_ci	struct f81232_private *priv = usb_get_serial_port_data(port);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	mutex_lock(&priv->lock);
2308c2ecf20Sopenharmony_ci	status = f81232_get_register(port, MODEM_STATUS_REGISTER,
2318c2ecf20Sopenharmony_ci			&current_msr);
2328c2ecf20Sopenharmony_ci	if (status) {
2338c2ecf20Sopenharmony_ci		dev_err(&port->dev, "%s fail, status: %d\n", __func__, status);
2348c2ecf20Sopenharmony_ci		mutex_unlock(&priv->lock);
2358c2ecf20Sopenharmony_ci		return;
2368c2ecf20Sopenharmony_ci	}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	if (!(current_msr & UART_MSR_ANY_DELTA)) {
2398c2ecf20Sopenharmony_ci		mutex_unlock(&priv->lock);
2408c2ecf20Sopenharmony_ci		return;
2418c2ecf20Sopenharmony_ci	}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	priv->modem_status = current_msr;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	if (current_msr & UART_MSR_DCTS)
2468c2ecf20Sopenharmony_ci		port->icount.cts++;
2478c2ecf20Sopenharmony_ci	if (current_msr & UART_MSR_DDSR)
2488c2ecf20Sopenharmony_ci		port->icount.dsr++;
2498c2ecf20Sopenharmony_ci	if (current_msr & UART_MSR_TERI)
2508c2ecf20Sopenharmony_ci		port->icount.rng++;
2518c2ecf20Sopenharmony_ci	if (current_msr & UART_MSR_DDCD) {
2528c2ecf20Sopenharmony_ci		port->icount.dcd++;
2538c2ecf20Sopenharmony_ci		tty = tty_port_tty_get(&port->port);
2548c2ecf20Sopenharmony_ci		if (tty) {
2558c2ecf20Sopenharmony_ci			usb_serial_handle_dcd_change(port, tty,
2568c2ecf20Sopenharmony_ci					current_msr & UART_MSR_DCD);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci			tty_kref_put(tty);
2598c2ecf20Sopenharmony_ci		}
2608c2ecf20Sopenharmony_ci	}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	wake_up_interruptible(&port->port.delta_msr_wait);
2638c2ecf20Sopenharmony_ci	mutex_unlock(&priv->lock);
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_cistatic int f81232_set_mctrl(struct usb_serial_port *port,
2678c2ecf20Sopenharmony_ci					   unsigned int set, unsigned int clear)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	u8 val;
2708c2ecf20Sopenharmony_ci	int status;
2718c2ecf20Sopenharmony_ci	struct f81232_private *priv = usb_get_serial_port_data(port);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	if (((set | clear) & (TIOCM_DTR | TIOCM_RTS)) == 0)
2748c2ecf20Sopenharmony_ci		return 0;	/* no change */
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	/* 'set' takes precedence over 'clear' */
2778c2ecf20Sopenharmony_ci	clear &= ~set;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	/* force enable interrupt with OUT2 */
2808c2ecf20Sopenharmony_ci	mutex_lock(&priv->lock);
2818c2ecf20Sopenharmony_ci	val = UART_MCR_OUT2 | priv->modem_control;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	if (clear & TIOCM_DTR)
2848c2ecf20Sopenharmony_ci		val &= ~UART_MCR_DTR;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	if (clear & TIOCM_RTS)
2878c2ecf20Sopenharmony_ci		val &= ~UART_MCR_RTS;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	if (set & TIOCM_DTR)
2908c2ecf20Sopenharmony_ci		val |= UART_MCR_DTR;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	if (set & TIOCM_RTS)
2938c2ecf20Sopenharmony_ci		val |= UART_MCR_RTS;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	dev_dbg(&port->dev, "%s new:%02x old:%02x\n", __func__,
2968c2ecf20Sopenharmony_ci			val, priv->modem_control);
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	status = f81232_set_register(port, MODEM_CONTROL_REGISTER, val);
2998c2ecf20Sopenharmony_ci	if (status) {
3008c2ecf20Sopenharmony_ci		dev_err(&port->dev, "%s set MCR status < 0\n", __func__);
3018c2ecf20Sopenharmony_ci		mutex_unlock(&priv->lock);
3028c2ecf20Sopenharmony_ci		return status;
3038c2ecf20Sopenharmony_ci	}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	priv->modem_control = val;
3068c2ecf20Sopenharmony_ci	mutex_unlock(&priv->lock);
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	return 0;
3098c2ecf20Sopenharmony_ci}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_cistatic void f81232_update_line_status(struct usb_serial_port *port,
3128c2ecf20Sopenharmony_ci				      unsigned char *data,
3138c2ecf20Sopenharmony_ci				      size_t actual_length)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	struct f81232_private *priv = usb_get_serial_port_data(port);
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	if (!actual_length)
3188c2ecf20Sopenharmony_ci		return;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	switch (data[0] & 0x07) {
3218c2ecf20Sopenharmony_ci	case 0x00: /* msr change */
3228c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "IIR: MSR Change: %02x\n", data[0]);
3238c2ecf20Sopenharmony_ci		schedule_work(&priv->interrupt_work);
3248c2ecf20Sopenharmony_ci		break;
3258c2ecf20Sopenharmony_ci	case 0x02: /* tx-empty */
3268c2ecf20Sopenharmony_ci		break;
3278c2ecf20Sopenharmony_ci	case 0x04: /* rx data available */
3288c2ecf20Sopenharmony_ci		break;
3298c2ecf20Sopenharmony_ci	case 0x06: /* lsr change */
3308c2ecf20Sopenharmony_ci		/* we can forget it. the LSR will read from bulk-in */
3318c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "IIR: LSR Change: %02x\n", data[0]);
3328c2ecf20Sopenharmony_ci		break;
3338c2ecf20Sopenharmony_ci	}
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_cistatic void f81232_read_int_callback(struct urb *urb)
3378c2ecf20Sopenharmony_ci{
3388c2ecf20Sopenharmony_ci	struct usb_serial_port *port =  urb->context;
3398c2ecf20Sopenharmony_ci	unsigned char *data = urb->transfer_buffer;
3408c2ecf20Sopenharmony_ci	unsigned int actual_length = urb->actual_length;
3418c2ecf20Sopenharmony_ci	int status = urb->status;
3428c2ecf20Sopenharmony_ci	int retval;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	switch (status) {
3458c2ecf20Sopenharmony_ci	case 0:
3468c2ecf20Sopenharmony_ci		/* success */
3478c2ecf20Sopenharmony_ci		break;
3488c2ecf20Sopenharmony_ci	case -ECONNRESET:
3498c2ecf20Sopenharmony_ci	case -ENOENT:
3508c2ecf20Sopenharmony_ci	case -ESHUTDOWN:
3518c2ecf20Sopenharmony_ci		/* this urb is terminated, clean up */
3528c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - urb shutting down with status: %d\n",
3538c2ecf20Sopenharmony_ci			__func__, status);
3548c2ecf20Sopenharmony_ci		return;
3558c2ecf20Sopenharmony_ci	default:
3568c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - nonzero urb status received: %d\n",
3578c2ecf20Sopenharmony_ci			__func__, status);
3588c2ecf20Sopenharmony_ci		goto exit;
3598c2ecf20Sopenharmony_ci	}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	usb_serial_debug_data(&port->dev, __func__,
3628c2ecf20Sopenharmony_ci			      urb->actual_length, urb->transfer_buffer);
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	f81232_update_line_status(port, data, actual_length);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ciexit:
3678c2ecf20Sopenharmony_ci	retval = usb_submit_urb(urb, GFP_ATOMIC);
3688c2ecf20Sopenharmony_ci	if (retval)
3698c2ecf20Sopenharmony_ci		dev_err(&urb->dev->dev,
3708c2ecf20Sopenharmony_ci			"%s - usb_submit_urb failed with result %d\n",
3718c2ecf20Sopenharmony_ci			__func__, retval);
3728c2ecf20Sopenharmony_ci}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_cistatic char f81232_handle_lsr(struct usb_serial_port *port, u8 lsr)
3758c2ecf20Sopenharmony_ci{
3768c2ecf20Sopenharmony_ci	struct f81232_private *priv = usb_get_serial_port_data(port);
3778c2ecf20Sopenharmony_ci	char tty_flag = TTY_NORMAL;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	if (!(lsr & UART_LSR_BRK_ERROR_BITS))
3808c2ecf20Sopenharmony_ci		return tty_flag;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	if (lsr & UART_LSR_BI) {
3838c2ecf20Sopenharmony_ci		tty_flag = TTY_BREAK;
3848c2ecf20Sopenharmony_ci		port->icount.brk++;
3858c2ecf20Sopenharmony_ci		usb_serial_handle_break(port);
3868c2ecf20Sopenharmony_ci	} else if (lsr & UART_LSR_PE) {
3878c2ecf20Sopenharmony_ci		tty_flag = TTY_PARITY;
3888c2ecf20Sopenharmony_ci		port->icount.parity++;
3898c2ecf20Sopenharmony_ci	} else if (lsr & UART_LSR_FE) {
3908c2ecf20Sopenharmony_ci		tty_flag = TTY_FRAME;
3918c2ecf20Sopenharmony_ci		port->icount.frame++;
3928c2ecf20Sopenharmony_ci	}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	if (lsr & UART_LSR_OE) {
3958c2ecf20Sopenharmony_ci		port->icount.overrun++;
3968c2ecf20Sopenharmony_ci		schedule_work(&priv->lsr_work);
3978c2ecf20Sopenharmony_ci		tty_insert_flip_char(&port->port, 0, TTY_OVERRUN);
3988c2ecf20Sopenharmony_ci	}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	return tty_flag;
4018c2ecf20Sopenharmony_ci}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_cistatic void f81232_process_read_urb(struct urb *urb)
4048c2ecf20Sopenharmony_ci{
4058c2ecf20Sopenharmony_ci	struct usb_serial_port *port = urb->context;
4068c2ecf20Sopenharmony_ci	unsigned char *data = urb->transfer_buffer;
4078c2ecf20Sopenharmony_ci	char tty_flag;
4088c2ecf20Sopenharmony_ci	unsigned int i;
4098c2ecf20Sopenharmony_ci	u8 lsr;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	/*
4128c2ecf20Sopenharmony_ci	 * When opening the port we get a 1-byte packet with the current LSR,
4138c2ecf20Sopenharmony_ci	 * which we discard.
4148c2ecf20Sopenharmony_ci	 */
4158c2ecf20Sopenharmony_ci	if ((urb->actual_length < 2) || (urb->actual_length % 2))
4168c2ecf20Sopenharmony_ci		return;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	/* bulk-in data: [LSR(1Byte)+DATA(1Byte)][LSR(1Byte)+DATA(1Byte)]... */
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	for (i = 0; i < urb->actual_length; i += 2) {
4218c2ecf20Sopenharmony_ci		lsr = data[i];
4228c2ecf20Sopenharmony_ci		tty_flag = f81232_handle_lsr(port, lsr);
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci		if (port->sysrq) {
4258c2ecf20Sopenharmony_ci			if (usb_serial_handle_sysrq_char(port, data[i + 1]))
4268c2ecf20Sopenharmony_ci				continue;
4278c2ecf20Sopenharmony_ci		}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci		tty_insert_flip_char(&port->port, data[i + 1], tty_flag);
4308c2ecf20Sopenharmony_ci	}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	tty_flip_buffer_push(&port->port);
4338c2ecf20Sopenharmony_ci}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_cistatic void f81534a_process_read_urb(struct urb *urb)
4368c2ecf20Sopenharmony_ci{
4378c2ecf20Sopenharmony_ci	struct usb_serial_port *port = urb->context;
4388c2ecf20Sopenharmony_ci	unsigned char *data = urb->transfer_buffer;
4398c2ecf20Sopenharmony_ci	char tty_flag;
4408c2ecf20Sopenharmony_ci	unsigned int i;
4418c2ecf20Sopenharmony_ci	u8 lsr;
4428c2ecf20Sopenharmony_ci	u8 len;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	if (urb->actual_length < 3) {
4458c2ecf20Sopenharmony_ci		dev_err(&port->dev, "short message received: %d\n",
4468c2ecf20Sopenharmony_ci				urb->actual_length);
4478c2ecf20Sopenharmony_ci		return;
4488c2ecf20Sopenharmony_ci	}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	len = data[0];
4518c2ecf20Sopenharmony_ci	if (len != urb->actual_length) {
4528c2ecf20Sopenharmony_ci		dev_err(&port->dev, "malformed message received: %d (%d)\n",
4538c2ecf20Sopenharmony_ci				urb->actual_length, len);
4548c2ecf20Sopenharmony_ci		return;
4558c2ecf20Sopenharmony_ci	}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	/* bulk-in data: [LEN][Data.....][LSR] */
4588c2ecf20Sopenharmony_ci	lsr = data[len - 1];
4598c2ecf20Sopenharmony_ci	tty_flag = f81232_handle_lsr(port, lsr);
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	if (port->sysrq) {
4628c2ecf20Sopenharmony_ci		for (i = 1; i < len - 1; ++i) {
4638c2ecf20Sopenharmony_ci			if (!usb_serial_handle_sysrq_char(port, data[i])) {
4648c2ecf20Sopenharmony_ci				tty_insert_flip_char(&port->port, data[i],
4658c2ecf20Sopenharmony_ci						tty_flag);
4668c2ecf20Sopenharmony_ci			}
4678c2ecf20Sopenharmony_ci		}
4688c2ecf20Sopenharmony_ci	} else {
4698c2ecf20Sopenharmony_ci		tty_insert_flip_string_fixed_flag(&port->port, &data[1],
4708c2ecf20Sopenharmony_ci							tty_flag, len - 2);
4718c2ecf20Sopenharmony_ci	}
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	tty_flip_buffer_push(&port->port);
4748c2ecf20Sopenharmony_ci}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_cistatic void f81232_break_ctl(struct tty_struct *tty, int break_state)
4778c2ecf20Sopenharmony_ci{
4788c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
4798c2ecf20Sopenharmony_ci	struct f81232_private *priv = usb_get_serial_port_data(port);
4808c2ecf20Sopenharmony_ci	int status;
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	mutex_lock(&priv->lock);
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	if (break_state)
4858c2ecf20Sopenharmony_ci		priv->shadow_lcr |= UART_LCR_SBC;
4868c2ecf20Sopenharmony_ci	else
4878c2ecf20Sopenharmony_ci		priv->shadow_lcr &= ~UART_LCR_SBC;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	status = f81232_set_register(port, LINE_CONTROL_REGISTER,
4908c2ecf20Sopenharmony_ci					priv->shadow_lcr);
4918c2ecf20Sopenharmony_ci	if (status)
4928c2ecf20Sopenharmony_ci		dev_err(&port->dev, "set break failed: %d\n", status);
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	mutex_unlock(&priv->lock);
4958c2ecf20Sopenharmony_ci}
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_cistatic int f81232_find_clk(speed_t baudrate)
4988c2ecf20Sopenharmony_ci{
4998c2ecf20Sopenharmony_ci	int idx;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	for (idx = 0; idx < ARRAY_SIZE(baudrate_table); ++idx) {
5028c2ecf20Sopenharmony_ci		if (baudrate <= baudrate_table[idx] &&
5038c2ecf20Sopenharmony_ci				baudrate_table[idx] % baudrate == 0)
5048c2ecf20Sopenharmony_ci			return idx;
5058c2ecf20Sopenharmony_ci	}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	return -EINVAL;
5088c2ecf20Sopenharmony_ci}
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_cistatic void f81232_set_baudrate(struct tty_struct *tty,
5118c2ecf20Sopenharmony_ci				struct usb_serial_port *port, speed_t baudrate,
5128c2ecf20Sopenharmony_ci				speed_t old_baudrate)
5138c2ecf20Sopenharmony_ci{
5148c2ecf20Sopenharmony_ci	struct f81232_private *priv = usb_get_serial_port_data(port);
5158c2ecf20Sopenharmony_ci	u8 lcr;
5168c2ecf20Sopenharmony_ci	int divisor;
5178c2ecf20Sopenharmony_ci	int status = 0;
5188c2ecf20Sopenharmony_ci	int i;
5198c2ecf20Sopenharmony_ci	int idx;
5208c2ecf20Sopenharmony_ci	speed_t baud_list[] = { baudrate, old_baudrate, F81232_DEF_BAUDRATE };
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(baud_list); ++i) {
5238c2ecf20Sopenharmony_ci		baudrate = baud_list[i];
5248c2ecf20Sopenharmony_ci		if (baudrate == 0) {
5258c2ecf20Sopenharmony_ci			tty_encode_baud_rate(tty, 0, 0);
5268c2ecf20Sopenharmony_ci			return;
5278c2ecf20Sopenharmony_ci		}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci		idx = f81232_find_clk(baudrate);
5308c2ecf20Sopenharmony_ci		if (idx >= 0) {
5318c2ecf20Sopenharmony_ci			tty_encode_baud_rate(tty, baudrate, baudrate);
5328c2ecf20Sopenharmony_ci			break;
5338c2ecf20Sopenharmony_ci		}
5348c2ecf20Sopenharmony_ci	}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	if (idx < 0)
5378c2ecf20Sopenharmony_ci		return;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	priv->baud_base = baudrate_table[idx];
5408c2ecf20Sopenharmony_ci	divisor = calc_baud_divisor(baudrate, priv->baud_base);
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	status = f81232_set_mask_register(port, F81232_CLK_REGISTER,
5438c2ecf20Sopenharmony_ci			F81232_CLK_MASK, clock_table[idx]);
5448c2ecf20Sopenharmony_ci	if (status) {
5458c2ecf20Sopenharmony_ci		dev_err(&port->dev, "%s failed to set CLK_REG: %d\n",
5468c2ecf20Sopenharmony_ci			__func__, status);
5478c2ecf20Sopenharmony_ci		return;
5488c2ecf20Sopenharmony_ci	}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	status = f81232_get_register(port, LINE_CONTROL_REGISTER,
5518c2ecf20Sopenharmony_ci			 &lcr); /* get LCR */
5528c2ecf20Sopenharmony_ci	if (status) {
5538c2ecf20Sopenharmony_ci		dev_err(&port->dev, "%s failed to get LCR: %d\n",
5548c2ecf20Sopenharmony_ci			__func__, status);
5558c2ecf20Sopenharmony_ci		return;
5568c2ecf20Sopenharmony_ci	}
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	status = f81232_set_register(port, LINE_CONTROL_REGISTER,
5598c2ecf20Sopenharmony_ci			 lcr | UART_LCR_DLAB); /* Enable DLAB */
5608c2ecf20Sopenharmony_ci	if (status) {
5618c2ecf20Sopenharmony_ci		dev_err(&port->dev, "%s failed to set DLAB: %d\n",
5628c2ecf20Sopenharmony_ci			__func__, status);
5638c2ecf20Sopenharmony_ci		return;
5648c2ecf20Sopenharmony_ci	}
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	status = f81232_set_register(port, RECEIVE_BUFFER_REGISTER,
5678c2ecf20Sopenharmony_ci			 divisor & 0x00ff); /* low */
5688c2ecf20Sopenharmony_ci	if (status) {
5698c2ecf20Sopenharmony_ci		dev_err(&port->dev, "%s failed to set baudrate MSB: %d\n",
5708c2ecf20Sopenharmony_ci			__func__, status);
5718c2ecf20Sopenharmony_ci		goto reapply_lcr;
5728c2ecf20Sopenharmony_ci	}
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	status = f81232_set_register(port, INTERRUPT_ENABLE_REGISTER,
5758c2ecf20Sopenharmony_ci			 (divisor & 0xff00) >> 8); /* high */
5768c2ecf20Sopenharmony_ci	if (status) {
5778c2ecf20Sopenharmony_ci		dev_err(&port->dev, "%s failed to set baudrate LSB: %d\n",
5788c2ecf20Sopenharmony_ci			__func__, status);
5798c2ecf20Sopenharmony_ci	}
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_cireapply_lcr:
5828c2ecf20Sopenharmony_ci	status = f81232_set_register(port, LINE_CONTROL_REGISTER,
5838c2ecf20Sopenharmony_ci			lcr & ~UART_LCR_DLAB);
5848c2ecf20Sopenharmony_ci	if (status) {
5858c2ecf20Sopenharmony_ci		dev_err(&port->dev, "%s failed to set DLAB: %d\n",
5868c2ecf20Sopenharmony_ci			__func__, status);
5878c2ecf20Sopenharmony_ci	}
5888c2ecf20Sopenharmony_ci}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_cistatic int f81232_port_enable(struct usb_serial_port *port)
5918c2ecf20Sopenharmony_ci{
5928c2ecf20Sopenharmony_ci	u8 val;
5938c2ecf20Sopenharmony_ci	int status;
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	/* fifo on, trigger8, clear TX/RX*/
5968c2ecf20Sopenharmony_ci	val = UART_FCR_TRIGGER_8 | UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR |
5978c2ecf20Sopenharmony_ci			UART_FCR_CLEAR_XMIT;
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	status = f81232_set_register(port, FIFO_CONTROL_REGISTER, val);
6008c2ecf20Sopenharmony_ci	if (status) {
6018c2ecf20Sopenharmony_ci		dev_err(&port->dev, "%s failed to set FCR: %d\n",
6028c2ecf20Sopenharmony_ci			__func__, status);
6038c2ecf20Sopenharmony_ci		return status;
6048c2ecf20Sopenharmony_ci	}
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	/* MSR Interrupt only, LSR will read from Bulk-in odd byte */
6078c2ecf20Sopenharmony_ci	status = f81232_set_register(port, INTERRUPT_ENABLE_REGISTER,
6088c2ecf20Sopenharmony_ci			UART_IER_MSI);
6098c2ecf20Sopenharmony_ci	if (status) {
6108c2ecf20Sopenharmony_ci		dev_err(&port->dev, "%s failed to set IER: %d\n",
6118c2ecf20Sopenharmony_ci			__func__, status);
6128c2ecf20Sopenharmony_ci		return status;
6138c2ecf20Sopenharmony_ci	}
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	return 0;
6168c2ecf20Sopenharmony_ci}
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_cistatic int f81232_port_disable(struct usb_serial_port *port)
6198c2ecf20Sopenharmony_ci{
6208c2ecf20Sopenharmony_ci	int status;
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	status = f81232_set_register(port, INTERRUPT_ENABLE_REGISTER, 0);
6238c2ecf20Sopenharmony_ci	if (status) {
6248c2ecf20Sopenharmony_ci		dev_err(&port->dev, "%s failed to set IER: %d\n",
6258c2ecf20Sopenharmony_ci			__func__, status);
6268c2ecf20Sopenharmony_ci		return status;
6278c2ecf20Sopenharmony_ci	}
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	return 0;
6308c2ecf20Sopenharmony_ci}
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_cistatic void f81232_set_termios(struct tty_struct *tty,
6338c2ecf20Sopenharmony_ci		struct usb_serial_port *port, struct ktermios *old_termios)
6348c2ecf20Sopenharmony_ci{
6358c2ecf20Sopenharmony_ci	struct f81232_private *priv = usb_get_serial_port_data(port);
6368c2ecf20Sopenharmony_ci	u8 new_lcr = 0;
6378c2ecf20Sopenharmony_ci	int status = 0;
6388c2ecf20Sopenharmony_ci	speed_t baudrate;
6398c2ecf20Sopenharmony_ci	speed_t old_baud;
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	/* Don't change anything if nothing has changed */
6428c2ecf20Sopenharmony_ci	if (old_termios && !tty_termios_hw_change(&tty->termios, old_termios))
6438c2ecf20Sopenharmony_ci		return;
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	if (C_BAUD(tty) == B0)
6468c2ecf20Sopenharmony_ci		f81232_set_mctrl(port, 0, TIOCM_DTR | TIOCM_RTS);
6478c2ecf20Sopenharmony_ci	else if (old_termios && (old_termios->c_cflag & CBAUD) == B0)
6488c2ecf20Sopenharmony_ci		f81232_set_mctrl(port, TIOCM_DTR | TIOCM_RTS, 0);
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	baudrate = tty_get_baud_rate(tty);
6518c2ecf20Sopenharmony_ci	if (baudrate > 0) {
6528c2ecf20Sopenharmony_ci		if (old_termios)
6538c2ecf20Sopenharmony_ci			old_baud = tty_termios_baud_rate(old_termios);
6548c2ecf20Sopenharmony_ci		else
6558c2ecf20Sopenharmony_ci			old_baud = F81232_DEF_BAUDRATE;
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci		f81232_set_baudrate(tty, port, baudrate, old_baud);
6588c2ecf20Sopenharmony_ci	}
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	if (C_PARENB(tty)) {
6618c2ecf20Sopenharmony_ci		new_lcr |= UART_LCR_PARITY;
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci		if (!C_PARODD(tty))
6648c2ecf20Sopenharmony_ci			new_lcr |= UART_LCR_EPAR;
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci		if (C_CMSPAR(tty))
6678c2ecf20Sopenharmony_ci			new_lcr |= UART_LCR_SPAR;
6688c2ecf20Sopenharmony_ci	}
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	if (C_CSTOPB(tty))
6718c2ecf20Sopenharmony_ci		new_lcr |= UART_LCR_STOP;
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	switch (C_CSIZE(tty)) {
6748c2ecf20Sopenharmony_ci	case CS5:
6758c2ecf20Sopenharmony_ci		new_lcr |= UART_LCR_WLEN5;
6768c2ecf20Sopenharmony_ci		break;
6778c2ecf20Sopenharmony_ci	case CS6:
6788c2ecf20Sopenharmony_ci		new_lcr |= UART_LCR_WLEN6;
6798c2ecf20Sopenharmony_ci		break;
6808c2ecf20Sopenharmony_ci	case CS7:
6818c2ecf20Sopenharmony_ci		new_lcr |= UART_LCR_WLEN7;
6828c2ecf20Sopenharmony_ci		break;
6838c2ecf20Sopenharmony_ci	default:
6848c2ecf20Sopenharmony_ci	case CS8:
6858c2ecf20Sopenharmony_ci		new_lcr |= UART_LCR_WLEN8;
6868c2ecf20Sopenharmony_ci		break;
6878c2ecf20Sopenharmony_ci	}
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	mutex_lock(&priv->lock);
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	new_lcr |= (priv->shadow_lcr & UART_LCR_SBC);
6928c2ecf20Sopenharmony_ci	status = f81232_set_register(port, LINE_CONTROL_REGISTER, new_lcr);
6938c2ecf20Sopenharmony_ci	if (status) {
6948c2ecf20Sopenharmony_ci		dev_err(&port->dev, "%s failed to set LCR: %d\n",
6958c2ecf20Sopenharmony_ci			__func__, status);
6968c2ecf20Sopenharmony_ci	}
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	priv->shadow_lcr = new_lcr;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	mutex_unlock(&priv->lock);
7018c2ecf20Sopenharmony_ci}
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_cistatic int f81232_tiocmget(struct tty_struct *tty)
7048c2ecf20Sopenharmony_ci{
7058c2ecf20Sopenharmony_ci	int r;
7068c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
7078c2ecf20Sopenharmony_ci	struct f81232_private *port_priv = usb_get_serial_port_data(port);
7088c2ecf20Sopenharmony_ci	u8 mcr, msr;
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	/* force get current MSR changed state */
7118c2ecf20Sopenharmony_ci	f81232_read_msr(port);
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	mutex_lock(&port_priv->lock);
7148c2ecf20Sopenharmony_ci	mcr = port_priv->modem_control;
7158c2ecf20Sopenharmony_ci	msr = port_priv->modem_status;
7168c2ecf20Sopenharmony_ci	mutex_unlock(&port_priv->lock);
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	r = (mcr & UART_MCR_DTR ? TIOCM_DTR : 0) |
7198c2ecf20Sopenharmony_ci		(mcr & UART_MCR_RTS ? TIOCM_RTS : 0) |
7208c2ecf20Sopenharmony_ci		(msr & UART_MSR_CTS ? TIOCM_CTS : 0) |
7218c2ecf20Sopenharmony_ci		(msr & UART_MSR_DCD ? TIOCM_CAR : 0) |
7228c2ecf20Sopenharmony_ci		(msr & UART_MSR_RI ? TIOCM_RI : 0) |
7238c2ecf20Sopenharmony_ci		(msr & UART_MSR_DSR ? TIOCM_DSR : 0);
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	return r;
7268c2ecf20Sopenharmony_ci}
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_cistatic int f81232_tiocmset(struct tty_struct *tty,
7298c2ecf20Sopenharmony_ci			unsigned int set, unsigned int clear)
7308c2ecf20Sopenharmony_ci{
7318c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	return f81232_set_mctrl(port, set, clear);
7348c2ecf20Sopenharmony_ci}
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_cistatic int f81232_open(struct tty_struct *tty, struct usb_serial_port *port)
7378c2ecf20Sopenharmony_ci{
7388c2ecf20Sopenharmony_ci	int result;
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	result = f81232_port_enable(port);
7418c2ecf20Sopenharmony_ci	if (result)
7428c2ecf20Sopenharmony_ci		return result;
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	/* Setup termios */
7458c2ecf20Sopenharmony_ci	if (tty)
7468c2ecf20Sopenharmony_ci		f81232_set_termios(tty, port, NULL);
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
7498c2ecf20Sopenharmony_ci	if (result) {
7508c2ecf20Sopenharmony_ci		dev_err(&port->dev, "%s - failed submitting interrupt urb,"
7518c2ecf20Sopenharmony_ci			" error %d\n", __func__, result);
7528c2ecf20Sopenharmony_ci		return result;
7538c2ecf20Sopenharmony_ci	}
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	result = usb_serial_generic_open(tty, port);
7568c2ecf20Sopenharmony_ci	if (result) {
7578c2ecf20Sopenharmony_ci		usb_kill_urb(port->interrupt_in_urb);
7588c2ecf20Sopenharmony_ci		return result;
7598c2ecf20Sopenharmony_ci	}
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	return 0;
7628c2ecf20Sopenharmony_ci}
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_cistatic int f81534a_open(struct tty_struct *tty, struct usb_serial_port *port)
7658c2ecf20Sopenharmony_ci{
7668c2ecf20Sopenharmony_ci	int status;
7678c2ecf20Sopenharmony_ci	u8 mask;
7688c2ecf20Sopenharmony_ci	u8 val;
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	val = F81534A_TRIGGER_MULTIPLE_4X | F81534A_FIFO_128BYTE;
7718c2ecf20Sopenharmony_ci	mask = F81534A_TRIGGER_MASK | F81534A_FIFO_128BYTE;
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	status = f81232_set_mask_register(port, F81534A_MODE_REG, mask, val);
7748c2ecf20Sopenharmony_ci	if (status) {
7758c2ecf20Sopenharmony_ci		dev_err(&port->dev, "failed to set MODE_REG: %d\n", status);
7768c2ecf20Sopenharmony_ci		return status;
7778c2ecf20Sopenharmony_ci	}
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	return f81232_open(tty, port);
7808c2ecf20Sopenharmony_ci}
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_cistatic void f81232_close(struct usb_serial_port *port)
7838c2ecf20Sopenharmony_ci{
7848c2ecf20Sopenharmony_ci	struct f81232_private *port_priv = usb_get_serial_port_data(port);
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	f81232_port_disable(port);
7878c2ecf20Sopenharmony_ci	usb_serial_generic_close(port);
7888c2ecf20Sopenharmony_ci	usb_kill_urb(port->interrupt_in_urb);
7898c2ecf20Sopenharmony_ci	flush_work(&port_priv->interrupt_work);
7908c2ecf20Sopenharmony_ci	flush_work(&port_priv->lsr_work);
7918c2ecf20Sopenharmony_ci}
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_cistatic void f81232_dtr_rts(struct usb_serial_port *port, int on)
7948c2ecf20Sopenharmony_ci{
7958c2ecf20Sopenharmony_ci	if (on)
7968c2ecf20Sopenharmony_ci		f81232_set_mctrl(port, TIOCM_DTR | TIOCM_RTS, 0);
7978c2ecf20Sopenharmony_ci	else
7988c2ecf20Sopenharmony_ci		f81232_set_mctrl(port, 0, TIOCM_DTR | TIOCM_RTS);
7998c2ecf20Sopenharmony_ci}
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_cistatic bool f81232_tx_empty(struct usb_serial_port *port)
8028c2ecf20Sopenharmony_ci{
8038c2ecf20Sopenharmony_ci	int status;
8048c2ecf20Sopenharmony_ci	u8 tmp;
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	status = f81232_get_register(port, LINE_STATUS_REGISTER, &tmp);
8078c2ecf20Sopenharmony_ci	if (!status) {
8088c2ecf20Sopenharmony_ci		if ((tmp & UART_LSR_TEMT) != UART_LSR_TEMT)
8098c2ecf20Sopenharmony_ci			return false;
8108c2ecf20Sopenharmony_ci	}
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	return true;
8138c2ecf20Sopenharmony_ci}
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_cistatic int f81232_carrier_raised(struct usb_serial_port *port)
8168c2ecf20Sopenharmony_ci{
8178c2ecf20Sopenharmony_ci	u8 msr;
8188c2ecf20Sopenharmony_ci	struct f81232_private *priv = usb_get_serial_port_data(port);
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	mutex_lock(&priv->lock);
8218c2ecf20Sopenharmony_ci	msr = priv->modem_status;
8228c2ecf20Sopenharmony_ci	mutex_unlock(&priv->lock);
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci	if (msr & UART_MSR_DCD)
8258c2ecf20Sopenharmony_ci		return 1;
8268c2ecf20Sopenharmony_ci	return 0;
8278c2ecf20Sopenharmony_ci}
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_cistatic int f81232_get_serial_info(struct tty_struct *tty,
8308c2ecf20Sopenharmony_ci		struct serial_struct *ss)
8318c2ecf20Sopenharmony_ci{
8328c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
8338c2ecf20Sopenharmony_ci	struct f81232_private *priv = usb_get_serial_port_data(port);
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	ss->type = PORT_16550A;
8368c2ecf20Sopenharmony_ci	ss->line = port->minor;
8378c2ecf20Sopenharmony_ci	ss->port = port->port_number;
8388c2ecf20Sopenharmony_ci	ss->baud_base = priv->baud_base;
8398c2ecf20Sopenharmony_ci	return 0;
8408c2ecf20Sopenharmony_ci}
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_cistatic void  f81232_interrupt_work(struct work_struct *work)
8438c2ecf20Sopenharmony_ci{
8448c2ecf20Sopenharmony_ci	struct f81232_private *priv =
8458c2ecf20Sopenharmony_ci		container_of(work, struct f81232_private, interrupt_work);
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	f81232_read_msr(priv->port);
8488c2ecf20Sopenharmony_ci}
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_cistatic void f81232_lsr_worker(struct work_struct *work)
8518c2ecf20Sopenharmony_ci{
8528c2ecf20Sopenharmony_ci	struct f81232_private *priv;
8538c2ecf20Sopenharmony_ci	struct usb_serial_port *port;
8548c2ecf20Sopenharmony_ci	int status;
8558c2ecf20Sopenharmony_ci	u8 tmp;
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci	priv = container_of(work, struct f81232_private, lsr_work);
8588c2ecf20Sopenharmony_ci	port = priv->port;
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci	status = f81232_get_register(port, LINE_STATUS_REGISTER, &tmp);
8618c2ecf20Sopenharmony_ci	if (status)
8628c2ecf20Sopenharmony_ci		dev_warn(&port->dev, "read LSR failed: %d\n", status);
8638c2ecf20Sopenharmony_ci}
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_cistatic int f81534a_ctrl_set_register(struct usb_interface *intf, u16 reg,
8668c2ecf20Sopenharmony_ci					u16 size, void *val)
8678c2ecf20Sopenharmony_ci{
8688c2ecf20Sopenharmony_ci	struct usb_device *dev = interface_to_usbdev(intf);
8698c2ecf20Sopenharmony_ci	int retry = F81534A_ACCESS_REG_RETRY;
8708c2ecf20Sopenharmony_ci	int status;
8718c2ecf20Sopenharmony_ci	u8 *tmp;
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci	tmp = kmemdup(val, size, GFP_KERNEL);
8748c2ecf20Sopenharmony_ci	if (!tmp)
8758c2ecf20Sopenharmony_ci		return -ENOMEM;
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	while (retry--) {
8788c2ecf20Sopenharmony_ci		status = usb_control_msg(dev,
8798c2ecf20Sopenharmony_ci					usb_sndctrlpipe(dev, 0),
8808c2ecf20Sopenharmony_ci					F81232_REGISTER_REQUEST,
8818c2ecf20Sopenharmony_ci					F81232_SET_REGISTER,
8828c2ecf20Sopenharmony_ci					reg,
8838c2ecf20Sopenharmony_ci					0,
8848c2ecf20Sopenharmony_ci					tmp,
8858c2ecf20Sopenharmony_ci					size,
8868c2ecf20Sopenharmony_ci					USB_CTRL_SET_TIMEOUT);
8878c2ecf20Sopenharmony_ci		if (status < 0) {
8888c2ecf20Sopenharmony_ci			status = usb_translate_errors(status);
8898c2ecf20Sopenharmony_ci			if (status == -EIO)
8908c2ecf20Sopenharmony_ci				continue;
8918c2ecf20Sopenharmony_ci		} else if (status != size) {
8928c2ecf20Sopenharmony_ci			/* Retry on short transfers */
8938c2ecf20Sopenharmony_ci			status = -EIO;
8948c2ecf20Sopenharmony_ci			continue;
8958c2ecf20Sopenharmony_ci		} else {
8968c2ecf20Sopenharmony_ci			status = 0;
8978c2ecf20Sopenharmony_ci		}
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci		break;
9008c2ecf20Sopenharmony_ci	}
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci	if (status) {
9038c2ecf20Sopenharmony_ci		dev_err(&intf->dev, "failed to set register 0x%x: %d\n",
9048c2ecf20Sopenharmony_ci				reg, status);
9058c2ecf20Sopenharmony_ci	}
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci	kfree(tmp);
9088c2ecf20Sopenharmony_ci	return status;
9098c2ecf20Sopenharmony_ci}
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_cistatic int f81534a_ctrl_enable_all_ports(struct usb_interface *intf, bool en)
9128c2ecf20Sopenharmony_ci{
9138c2ecf20Sopenharmony_ci	unsigned char enable[2] = {0};
9148c2ecf20Sopenharmony_ci	int status;
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci	/*
9178c2ecf20Sopenharmony_ci	 * Enable all available serial ports, define as following:
9188c2ecf20Sopenharmony_ci	 * bit 15	: Reset behavior (when HUB got soft reset)
9198c2ecf20Sopenharmony_ci	 *			0: maintain all serial port enabled state.
9208c2ecf20Sopenharmony_ci	 *			1: disable all serial port.
9218c2ecf20Sopenharmony_ci	 * bit 0~11	: Serial port enable bit.
9228c2ecf20Sopenharmony_ci	 */
9238c2ecf20Sopenharmony_ci	if (en) {
9248c2ecf20Sopenharmony_ci		enable[0] = 0xff;
9258c2ecf20Sopenharmony_ci		enable[1] = 0x8f;
9268c2ecf20Sopenharmony_ci	}
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci	status = f81534a_ctrl_set_register(intf, F81534A_CTRL_CMD_ENABLE_PORT,
9298c2ecf20Sopenharmony_ci			sizeof(enable), enable);
9308c2ecf20Sopenharmony_ci	if (status)
9318c2ecf20Sopenharmony_ci		dev_err(&intf->dev, "failed to enable ports: %d\n", status);
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	return status;
9348c2ecf20Sopenharmony_ci}
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_cistatic int f81534a_ctrl_probe(struct usb_interface *intf,
9378c2ecf20Sopenharmony_ci				const struct usb_device_id *id)
9388c2ecf20Sopenharmony_ci{
9398c2ecf20Sopenharmony_ci	return f81534a_ctrl_enable_all_ports(intf, true);
9408c2ecf20Sopenharmony_ci}
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_cistatic void f81534a_ctrl_disconnect(struct usb_interface *intf)
9438c2ecf20Sopenharmony_ci{
9448c2ecf20Sopenharmony_ci	f81534a_ctrl_enable_all_ports(intf, false);
9458c2ecf20Sopenharmony_ci}
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_cistatic int f81534a_ctrl_resume(struct usb_interface *intf)
9488c2ecf20Sopenharmony_ci{
9498c2ecf20Sopenharmony_ci	return f81534a_ctrl_enable_all_ports(intf, true);
9508c2ecf20Sopenharmony_ci}
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_cistatic int f81232_port_probe(struct usb_serial_port *port)
9538c2ecf20Sopenharmony_ci{
9548c2ecf20Sopenharmony_ci	struct f81232_private *priv;
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci	priv = devm_kzalloc(&port->dev, sizeof(*priv), GFP_KERNEL);
9578c2ecf20Sopenharmony_ci	if (!priv)
9588c2ecf20Sopenharmony_ci		return -ENOMEM;
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci	mutex_init(&priv->lock);
9618c2ecf20Sopenharmony_ci	INIT_WORK(&priv->interrupt_work,  f81232_interrupt_work);
9628c2ecf20Sopenharmony_ci	INIT_WORK(&priv->lsr_work, f81232_lsr_worker);
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci	usb_set_serial_port_data(port, priv);
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_ci	port->port.drain_delay = 256;
9678c2ecf20Sopenharmony_ci	priv->port = port;
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci	return 0;
9708c2ecf20Sopenharmony_ci}
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_cistatic int f81534a_port_probe(struct usb_serial_port *port)
9738c2ecf20Sopenharmony_ci{
9748c2ecf20Sopenharmony_ci	int status;
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci	/* tri-state with pull-high, default RS232 Mode */
9778c2ecf20Sopenharmony_ci	status = f81232_set_register(port, F81534A_GPIO_REG,
9788c2ecf20Sopenharmony_ci					F81534A_GPIO_MODE2_DIR);
9798c2ecf20Sopenharmony_ci	if (status)
9808c2ecf20Sopenharmony_ci		return status;
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	return f81232_port_probe(port);
9838c2ecf20Sopenharmony_ci}
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_cistatic int f81232_suspend(struct usb_serial *serial, pm_message_t message)
9868c2ecf20Sopenharmony_ci{
9878c2ecf20Sopenharmony_ci	struct usb_serial_port *port = serial->port[0];
9888c2ecf20Sopenharmony_ci	struct f81232_private *port_priv = usb_get_serial_port_data(port);
9898c2ecf20Sopenharmony_ci	int i;
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i)
9928c2ecf20Sopenharmony_ci		usb_kill_urb(port->read_urbs[i]);
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci	usb_kill_urb(port->interrupt_in_urb);
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci	if (port_priv) {
9978c2ecf20Sopenharmony_ci		flush_work(&port_priv->interrupt_work);
9988c2ecf20Sopenharmony_ci		flush_work(&port_priv->lsr_work);
9998c2ecf20Sopenharmony_ci	}
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	return 0;
10028c2ecf20Sopenharmony_ci}
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_cistatic int f81232_resume(struct usb_serial *serial)
10058c2ecf20Sopenharmony_ci{
10068c2ecf20Sopenharmony_ci	struct usb_serial_port *port = serial->port[0];
10078c2ecf20Sopenharmony_ci	int result;
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci	if (tty_port_initialized(&port->port)) {
10108c2ecf20Sopenharmony_ci		result = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO);
10118c2ecf20Sopenharmony_ci		if (result) {
10128c2ecf20Sopenharmony_ci			dev_err(&port->dev, "submit interrupt urb failed: %d\n",
10138c2ecf20Sopenharmony_ci					result);
10148c2ecf20Sopenharmony_ci			return result;
10158c2ecf20Sopenharmony_ci		}
10168c2ecf20Sopenharmony_ci	}
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci	return usb_serial_generic_resume(serial);
10198c2ecf20Sopenharmony_ci}
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_cistatic struct usb_serial_driver f81232_device = {
10228c2ecf20Sopenharmony_ci	.driver = {
10238c2ecf20Sopenharmony_ci		.owner =	THIS_MODULE,
10248c2ecf20Sopenharmony_ci		.name =		"f81232",
10258c2ecf20Sopenharmony_ci	},
10268c2ecf20Sopenharmony_ci	.id_table =		f81232_id_table,
10278c2ecf20Sopenharmony_ci	.num_ports =		1,
10288c2ecf20Sopenharmony_ci	.bulk_in_size =		256,
10298c2ecf20Sopenharmony_ci	.bulk_out_size =	256,
10308c2ecf20Sopenharmony_ci	.open =			f81232_open,
10318c2ecf20Sopenharmony_ci	.close =		f81232_close,
10328c2ecf20Sopenharmony_ci	.dtr_rts =		f81232_dtr_rts,
10338c2ecf20Sopenharmony_ci	.carrier_raised =	f81232_carrier_raised,
10348c2ecf20Sopenharmony_ci	.get_serial =		f81232_get_serial_info,
10358c2ecf20Sopenharmony_ci	.break_ctl =		f81232_break_ctl,
10368c2ecf20Sopenharmony_ci	.set_termios =		f81232_set_termios,
10378c2ecf20Sopenharmony_ci	.tiocmget =		f81232_tiocmget,
10388c2ecf20Sopenharmony_ci	.tiocmset =		f81232_tiocmset,
10398c2ecf20Sopenharmony_ci	.tiocmiwait =		usb_serial_generic_tiocmiwait,
10408c2ecf20Sopenharmony_ci	.tx_empty =		f81232_tx_empty,
10418c2ecf20Sopenharmony_ci	.process_read_urb =	f81232_process_read_urb,
10428c2ecf20Sopenharmony_ci	.read_int_callback =	f81232_read_int_callback,
10438c2ecf20Sopenharmony_ci	.port_probe =		f81232_port_probe,
10448c2ecf20Sopenharmony_ci	.suspend =		f81232_suspend,
10458c2ecf20Sopenharmony_ci	.resume =		f81232_resume,
10468c2ecf20Sopenharmony_ci};
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_cistatic struct usb_serial_driver f81534a_device = {
10498c2ecf20Sopenharmony_ci	.driver = {
10508c2ecf20Sopenharmony_ci		.owner =	THIS_MODULE,
10518c2ecf20Sopenharmony_ci		.name =		"f81534a",
10528c2ecf20Sopenharmony_ci	},
10538c2ecf20Sopenharmony_ci	.id_table =		f81534a_id_table,
10548c2ecf20Sopenharmony_ci	.num_ports =		1,
10558c2ecf20Sopenharmony_ci	.open =			f81534a_open,
10568c2ecf20Sopenharmony_ci	.close =		f81232_close,
10578c2ecf20Sopenharmony_ci	.dtr_rts =		f81232_dtr_rts,
10588c2ecf20Sopenharmony_ci	.carrier_raised =	f81232_carrier_raised,
10598c2ecf20Sopenharmony_ci	.get_serial =		f81232_get_serial_info,
10608c2ecf20Sopenharmony_ci	.break_ctl =		f81232_break_ctl,
10618c2ecf20Sopenharmony_ci	.set_termios =		f81232_set_termios,
10628c2ecf20Sopenharmony_ci	.tiocmget =		f81232_tiocmget,
10638c2ecf20Sopenharmony_ci	.tiocmset =		f81232_tiocmset,
10648c2ecf20Sopenharmony_ci	.tiocmiwait =		usb_serial_generic_tiocmiwait,
10658c2ecf20Sopenharmony_ci	.tx_empty =		f81232_tx_empty,
10668c2ecf20Sopenharmony_ci	.process_read_urb =	f81534a_process_read_urb,
10678c2ecf20Sopenharmony_ci	.read_int_callback =	f81232_read_int_callback,
10688c2ecf20Sopenharmony_ci	.port_probe =		f81534a_port_probe,
10698c2ecf20Sopenharmony_ci	.suspend =		f81232_suspend,
10708c2ecf20Sopenharmony_ci	.resume =		f81232_resume,
10718c2ecf20Sopenharmony_ci};
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_cistatic struct usb_serial_driver * const serial_drivers[] = {
10748c2ecf20Sopenharmony_ci	&f81232_device,
10758c2ecf20Sopenharmony_ci	&f81534a_device,
10768c2ecf20Sopenharmony_ci	NULL,
10778c2ecf20Sopenharmony_ci};
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_cistatic struct usb_driver f81534a_ctrl_driver = {
10808c2ecf20Sopenharmony_ci	.name =		"f81534a_ctrl",
10818c2ecf20Sopenharmony_ci	.id_table =	f81534a_ctrl_id_table,
10828c2ecf20Sopenharmony_ci	.probe =	f81534a_ctrl_probe,
10838c2ecf20Sopenharmony_ci	.disconnect =	f81534a_ctrl_disconnect,
10848c2ecf20Sopenharmony_ci	.resume =	f81534a_ctrl_resume,
10858c2ecf20Sopenharmony_ci};
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_cistatic int __init f81232_init(void)
10888c2ecf20Sopenharmony_ci{
10898c2ecf20Sopenharmony_ci	int status;
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci	status = usb_register_driver(&f81534a_ctrl_driver, THIS_MODULE,
10928c2ecf20Sopenharmony_ci			KBUILD_MODNAME);
10938c2ecf20Sopenharmony_ci	if (status)
10948c2ecf20Sopenharmony_ci		return status;
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci	status = usb_serial_register_drivers(serial_drivers, KBUILD_MODNAME,
10978c2ecf20Sopenharmony_ci			combined_id_table);
10988c2ecf20Sopenharmony_ci	if (status) {
10998c2ecf20Sopenharmony_ci		usb_deregister(&f81534a_ctrl_driver);
11008c2ecf20Sopenharmony_ci		return status;
11018c2ecf20Sopenharmony_ci	}
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_ci	return 0;
11048c2ecf20Sopenharmony_ci}
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_cistatic void __exit f81232_exit(void)
11078c2ecf20Sopenharmony_ci{
11088c2ecf20Sopenharmony_ci	usb_serial_deregister_drivers(serial_drivers);
11098c2ecf20Sopenharmony_ci	usb_deregister(&f81534a_ctrl_driver);
11108c2ecf20Sopenharmony_ci}
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_cimodule_init(f81232_init);
11138c2ecf20Sopenharmony_cimodule_exit(f81232_exit);
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Fintek F81232/532A/534A/535/536 USB to serial driver");
11168c2ecf20Sopenharmony_ciMODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>");
11178c2ecf20Sopenharmony_ciMODULE_AUTHOR("Peter Hong <peter_hong@fintek.com.tw>");
11188c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
1119