162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Renesas Electronics uPD78F0730 USB to serial converter driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2014,2016 Maksim Salau <maksim.salau@gmail.com>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Protocol of the adaptor is described in the application note U19660EJ1V0AN00
862306a36Sopenharmony_ci * μPD78F0730 8-bit Single-Chip Microcontroller
962306a36Sopenharmony_ci * USB-to-Serial Conversion Software
1062306a36Sopenharmony_ci * <https://www.renesas.com/en-eu/doc/DocumentServer/026/U19660EJ1V0AN00.pdf>
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * The adaptor functionality is limited to the following:
1362306a36Sopenharmony_ci * - data bits: 7 or 8
1462306a36Sopenharmony_ci * - stop bits: 1 or 2
1562306a36Sopenharmony_ci * - parity: even, odd or none
1662306a36Sopenharmony_ci * - flow control: none
1762306a36Sopenharmony_ci * - baud rates: 0, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 153600
1862306a36Sopenharmony_ci * - signals: DTR, RTS and BREAK
1962306a36Sopenharmony_ci */
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include <linux/module.h>
2262306a36Sopenharmony_ci#include <linux/slab.h>
2362306a36Sopenharmony_ci#include <linux/tty.h>
2462306a36Sopenharmony_ci#include <linux/usb.h>
2562306a36Sopenharmony_ci#include <linux/usb/serial.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define DRIVER_DESC "Renesas uPD78F0730 USB to serial converter driver"
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define DRIVER_AUTHOR "Maksim Salau <maksim.salau@gmail.com>"
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic const struct usb_device_id id_table[] = {
3262306a36Sopenharmony_ci	{ USB_DEVICE(0x0409, 0x0063) }, /* V850ESJX3-STICK */
3362306a36Sopenharmony_ci	{ USB_DEVICE(0x045B, 0x0212) }, /* YRPBRL78G13, YRPBRL78G14 */
3462306a36Sopenharmony_ci	{ USB_DEVICE(0x064B, 0x7825) }, /* Analog Devices EVAL-ADXL362Z-DB */
3562306a36Sopenharmony_ci	{}
3662306a36Sopenharmony_ci};
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, id_table);
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci/*
4162306a36Sopenharmony_ci * Each adaptor is associated with a private structure, that holds the current
4262306a36Sopenharmony_ci * state of control signals (DTR, RTS and BREAK).
4362306a36Sopenharmony_ci */
4462306a36Sopenharmony_cistruct upd78f0730_port_private {
4562306a36Sopenharmony_ci	struct mutex	lock;		/* mutex to protect line_signals */
4662306a36Sopenharmony_ci	u8		line_signals;
4762306a36Sopenharmony_ci};
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/* Op-codes of control commands */
5062306a36Sopenharmony_ci#define UPD78F0730_CMD_LINE_CONTROL	0x00
5162306a36Sopenharmony_ci#define UPD78F0730_CMD_SET_DTR_RTS	0x01
5262306a36Sopenharmony_ci#define UPD78F0730_CMD_SET_XON_XOFF_CHR	0x02
5362306a36Sopenharmony_ci#define UPD78F0730_CMD_OPEN_CLOSE	0x03
5462306a36Sopenharmony_ci#define UPD78F0730_CMD_SET_ERR_CHR	0x04
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci/* Data sizes in UPD78F0730_CMD_LINE_CONTROL command */
5762306a36Sopenharmony_ci#define UPD78F0730_DATA_SIZE_7_BITS	0x00
5862306a36Sopenharmony_ci#define UPD78F0730_DATA_SIZE_8_BITS	0x01
5962306a36Sopenharmony_ci#define UPD78F0730_DATA_SIZE_MASK	0x01
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/* Stop-bit modes in UPD78F0730_CMD_LINE_CONTROL command */
6262306a36Sopenharmony_ci#define UPD78F0730_STOP_BIT_1_BIT	0x00
6362306a36Sopenharmony_ci#define UPD78F0730_STOP_BIT_2_BIT	0x02
6462306a36Sopenharmony_ci#define UPD78F0730_STOP_BIT_MASK	0x02
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci/* Parity modes in UPD78F0730_CMD_LINE_CONTROL command */
6762306a36Sopenharmony_ci#define UPD78F0730_PARITY_NONE	0x00
6862306a36Sopenharmony_ci#define UPD78F0730_PARITY_EVEN	0x04
6962306a36Sopenharmony_ci#define UPD78F0730_PARITY_ODD	0x08
7062306a36Sopenharmony_ci#define UPD78F0730_PARITY_MASK	0x0C
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci/* Flow control modes in UPD78F0730_CMD_LINE_CONTROL command */
7362306a36Sopenharmony_ci#define UPD78F0730_FLOW_CONTROL_NONE	0x00
7462306a36Sopenharmony_ci#define UPD78F0730_FLOW_CONTROL_HW	0x10
7562306a36Sopenharmony_ci#define UPD78F0730_FLOW_CONTROL_SW	0x20
7662306a36Sopenharmony_ci#define UPD78F0730_FLOW_CONTROL_MASK	0x30
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci/* Control signal bits in UPD78F0730_CMD_SET_DTR_RTS command */
7962306a36Sopenharmony_ci#define UPD78F0730_RTS		0x01
8062306a36Sopenharmony_ci#define UPD78F0730_DTR		0x02
8162306a36Sopenharmony_ci#define UPD78F0730_BREAK	0x04
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci/* Port modes in UPD78F0730_CMD_OPEN_CLOSE command */
8462306a36Sopenharmony_ci#define UPD78F0730_PORT_CLOSE	0x00
8562306a36Sopenharmony_ci#define UPD78F0730_PORT_OPEN	0x01
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci/* Error character substitution modes in UPD78F0730_CMD_SET_ERR_CHR command */
8862306a36Sopenharmony_ci#define UPD78F0730_ERR_CHR_DISABLED	0x00
8962306a36Sopenharmony_ci#define UPD78F0730_ERR_CHR_ENABLED	0x01
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci/*
9262306a36Sopenharmony_ci * Declaration of command structures
9362306a36Sopenharmony_ci */
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci/* UPD78F0730_CMD_LINE_CONTROL command */
9662306a36Sopenharmony_cistruct upd78f0730_line_control {
9762306a36Sopenharmony_ci	u8	opcode;
9862306a36Sopenharmony_ci	__le32	baud_rate;
9962306a36Sopenharmony_ci	u8	params;
10062306a36Sopenharmony_ci} __packed;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci/* UPD78F0730_CMD_SET_DTR_RTS command */
10362306a36Sopenharmony_cistruct upd78f0730_set_dtr_rts {
10462306a36Sopenharmony_ci	u8 opcode;
10562306a36Sopenharmony_ci	u8 params;
10662306a36Sopenharmony_ci};
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci/* UPD78F0730_CMD_SET_XON_OFF_CHR command */
10962306a36Sopenharmony_cistruct upd78f0730_set_xon_xoff_chr {
11062306a36Sopenharmony_ci	u8 opcode;
11162306a36Sopenharmony_ci	u8 xon;
11262306a36Sopenharmony_ci	u8 xoff;
11362306a36Sopenharmony_ci};
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci/* UPD78F0730_CMD_OPEN_CLOSE command */
11662306a36Sopenharmony_cistruct upd78f0730_open_close {
11762306a36Sopenharmony_ci	u8 opcode;
11862306a36Sopenharmony_ci	u8 state;
11962306a36Sopenharmony_ci};
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci/* UPD78F0730_CMD_SET_ERR_CHR command */
12262306a36Sopenharmony_cistruct upd78f0730_set_err_chr {
12362306a36Sopenharmony_ci	u8 opcode;
12462306a36Sopenharmony_ci	u8 state;
12562306a36Sopenharmony_ci	u8 err_char;
12662306a36Sopenharmony_ci};
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic int upd78f0730_send_ctl(struct usb_serial_port *port,
12962306a36Sopenharmony_ci			const void *data, int size)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	struct usb_device *usbdev = port->serial->dev;
13262306a36Sopenharmony_ci	void *buf;
13362306a36Sopenharmony_ci	int res;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	if (size <= 0 || !data)
13662306a36Sopenharmony_ci		return -EINVAL;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	buf = kmemdup(data, size, GFP_KERNEL);
13962306a36Sopenharmony_ci	if (!buf)
14062306a36Sopenharmony_ci		return -ENOMEM;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	res = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x00,
14362306a36Sopenharmony_ci			USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
14462306a36Sopenharmony_ci			0x0000, 0x0000, buf, size, USB_CTRL_SET_TIMEOUT);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	kfree(buf);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	if (res < 0) {
14962306a36Sopenharmony_ci		struct device *dev = &port->dev;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci		dev_err(dev, "failed to send control request %02x: %d\n",
15262306a36Sopenharmony_ci			*(u8 *)data, res);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci		return res;
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	return 0;
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic int upd78f0730_port_probe(struct usb_serial_port *port)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	struct upd78f0730_port_private *private;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	private = kzalloc(sizeof(*private), GFP_KERNEL);
16562306a36Sopenharmony_ci	if (!private)
16662306a36Sopenharmony_ci		return -ENOMEM;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	mutex_init(&private->lock);
16962306a36Sopenharmony_ci	usb_set_serial_port_data(port, private);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	return 0;
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cistatic void upd78f0730_port_remove(struct usb_serial_port *port)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	struct upd78f0730_port_private *private;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	private = usb_get_serial_port_data(port);
17962306a36Sopenharmony_ci	mutex_destroy(&private->lock);
18062306a36Sopenharmony_ci	kfree(private);
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cistatic int upd78f0730_tiocmget(struct tty_struct *tty)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	struct upd78f0730_port_private *private;
18662306a36Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
18762306a36Sopenharmony_ci	int signals;
18862306a36Sopenharmony_ci	int res;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	private = usb_get_serial_port_data(port);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	mutex_lock(&private->lock);
19362306a36Sopenharmony_ci	signals = private->line_signals;
19462306a36Sopenharmony_ci	mutex_unlock(&private->lock);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	res = ((signals & UPD78F0730_DTR) ? TIOCM_DTR : 0) |
19762306a36Sopenharmony_ci		((signals & UPD78F0730_RTS) ? TIOCM_RTS : 0);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	dev_dbg(&port->dev, "%s - res = %x\n", __func__, res);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	return res;
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic int upd78f0730_tiocmset(struct tty_struct *tty,
20562306a36Sopenharmony_ci			unsigned int set, unsigned int clear)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
20862306a36Sopenharmony_ci	struct upd78f0730_port_private *private;
20962306a36Sopenharmony_ci	struct upd78f0730_set_dtr_rts request;
21062306a36Sopenharmony_ci	struct device *dev = &port->dev;
21162306a36Sopenharmony_ci	int res;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	private = usb_get_serial_port_data(port);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	mutex_lock(&private->lock);
21662306a36Sopenharmony_ci	if (set & TIOCM_DTR) {
21762306a36Sopenharmony_ci		private->line_signals |= UPD78F0730_DTR;
21862306a36Sopenharmony_ci		dev_dbg(dev, "%s - set DTR\n", __func__);
21962306a36Sopenharmony_ci	}
22062306a36Sopenharmony_ci	if (set & TIOCM_RTS) {
22162306a36Sopenharmony_ci		private->line_signals |= UPD78F0730_RTS;
22262306a36Sopenharmony_ci		dev_dbg(dev, "%s - set RTS\n", __func__);
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci	if (clear & TIOCM_DTR) {
22562306a36Sopenharmony_ci		private->line_signals &= ~UPD78F0730_DTR;
22662306a36Sopenharmony_ci		dev_dbg(dev, "%s - clear DTR\n", __func__);
22762306a36Sopenharmony_ci	}
22862306a36Sopenharmony_ci	if (clear & TIOCM_RTS) {
22962306a36Sopenharmony_ci		private->line_signals &= ~UPD78F0730_RTS;
23062306a36Sopenharmony_ci		dev_dbg(dev, "%s - clear RTS\n", __func__);
23162306a36Sopenharmony_ci	}
23262306a36Sopenharmony_ci	request.opcode = UPD78F0730_CMD_SET_DTR_RTS;
23362306a36Sopenharmony_ci	request.params = private->line_signals;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	res = upd78f0730_send_ctl(port, &request, sizeof(request));
23662306a36Sopenharmony_ci	mutex_unlock(&private->lock);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	return res;
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistatic int upd78f0730_break_ctl(struct tty_struct *tty, int break_state)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	struct upd78f0730_port_private *private;
24462306a36Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
24562306a36Sopenharmony_ci	struct upd78f0730_set_dtr_rts request;
24662306a36Sopenharmony_ci	struct device *dev = &port->dev;
24762306a36Sopenharmony_ci	int res;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	private = usb_get_serial_port_data(port);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	mutex_lock(&private->lock);
25262306a36Sopenharmony_ci	if (break_state) {
25362306a36Sopenharmony_ci		private->line_signals |= UPD78F0730_BREAK;
25462306a36Sopenharmony_ci		dev_dbg(dev, "%s - set BREAK\n", __func__);
25562306a36Sopenharmony_ci	} else {
25662306a36Sopenharmony_ci		private->line_signals &= ~UPD78F0730_BREAK;
25762306a36Sopenharmony_ci		dev_dbg(dev, "%s - clear BREAK\n", __func__);
25862306a36Sopenharmony_ci	}
25962306a36Sopenharmony_ci	request.opcode = UPD78F0730_CMD_SET_DTR_RTS;
26062306a36Sopenharmony_ci	request.params = private->line_signals;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	res = upd78f0730_send_ctl(port, &request, sizeof(request));
26362306a36Sopenharmony_ci	mutex_unlock(&private->lock);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	return res;
26662306a36Sopenharmony_ci}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_cistatic void upd78f0730_dtr_rts(struct usb_serial_port *port, int on)
26962306a36Sopenharmony_ci{
27062306a36Sopenharmony_ci	struct tty_struct *tty = port->port.tty;
27162306a36Sopenharmony_ci	unsigned int set = 0;
27262306a36Sopenharmony_ci	unsigned int clear = 0;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	if (on)
27562306a36Sopenharmony_ci		set = TIOCM_DTR | TIOCM_RTS;
27662306a36Sopenharmony_ci	else
27762306a36Sopenharmony_ci		clear = TIOCM_DTR | TIOCM_RTS;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	upd78f0730_tiocmset(tty, set, clear);
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic speed_t upd78f0730_get_baud_rate(struct tty_struct *tty)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	const speed_t baud_rate = tty_get_baud_rate(tty);
28562306a36Sopenharmony_ci	static const speed_t supported[] = {
28662306a36Sopenharmony_ci		0, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 153600
28762306a36Sopenharmony_ci	};
28862306a36Sopenharmony_ci	int i;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	for (i = ARRAY_SIZE(supported) - 1; i >= 0; i--) {
29162306a36Sopenharmony_ci		if (baud_rate == supported[i])
29262306a36Sopenharmony_ci			return baud_rate;
29362306a36Sopenharmony_ci	}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	/* If the baud rate is not supported, switch to the default one */
29662306a36Sopenharmony_ci	tty_encode_baud_rate(tty, 9600, 9600);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	return tty_get_baud_rate(tty);
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_cistatic void upd78f0730_set_termios(struct tty_struct *tty,
30262306a36Sopenharmony_ci				   struct usb_serial_port *port,
30362306a36Sopenharmony_ci				   const struct ktermios *old_termios)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	struct device *dev = &port->dev;
30662306a36Sopenharmony_ci	struct upd78f0730_line_control request;
30762306a36Sopenharmony_ci	speed_t baud_rate;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	if (old_termios && !tty_termios_hw_change(&tty->termios, old_termios))
31062306a36Sopenharmony_ci		return;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	if (C_BAUD(tty) == B0)
31362306a36Sopenharmony_ci		upd78f0730_dtr_rts(port, 0);
31462306a36Sopenharmony_ci	else if (old_termios && (old_termios->c_cflag & CBAUD) == B0)
31562306a36Sopenharmony_ci		upd78f0730_dtr_rts(port, 1);
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	baud_rate = upd78f0730_get_baud_rate(tty);
31862306a36Sopenharmony_ci	request.opcode = UPD78F0730_CMD_LINE_CONTROL;
31962306a36Sopenharmony_ci	request.baud_rate = cpu_to_le32(baud_rate);
32062306a36Sopenharmony_ci	request.params = 0;
32162306a36Sopenharmony_ci	dev_dbg(dev, "%s - baud rate = %d\n", __func__, baud_rate);
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	switch (C_CSIZE(tty)) {
32462306a36Sopenharmony_ci	case CS7:
32562306a36Sopenharmony_ci		request.params |= UPD78F0730_DATA_SIZE_7_BITS;
32662306a36Sopenharmony_ci		dev_dbg(dev, "%s - 7 data bits\n", __func__);
32762306a36Sopenharmony_ci		break;
32862306a36Sopenharmony_ci	default:
32962306a36Sopenharmony_ci		tty->termios.c_cflag &= ~CSIZE;
33062306a36Sopenharmony_ci		tty->termios.c_cflag |= CS8;
33162306a36Sopenharmony_ci		dev_warn(dev, "data size is not supported, using 8 bits\n");
33262306a36Sopenharmony_ci		fallthrough;
33362306a36Sopenharmony_ci	case CS8:
33462306a36Sopenharmony_ci		request.params |= UPD78F0730_DATA_SIZE_8_BITS;
33562306a36Sopenharmony_ci		dev_dbg(dev, "%s - 8 data bits\n", __func__);
33662306a36Sopenharmony_ci		break;
33762306a36Sopenharmony_ci	}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	if (C_PARENB(tty)) {
34062306a36Sopenharmony_ci		if (C_PARODD(tty)) {
34162306a36Sopenharmony_ci			request.params |= UPD78F0730_PARITY_ODD;
34262306a36Sopenharmony_ci			dev_dbg(dev, "%s - odd parity\n", __func__);
34362306a36Sopenharmony_ci		} else {
34462306a36Sopenharmony_ci			request.params |= UPD78F0730_PARITY_EVEN;
34562306a36Sopenharmony_ci			dev_dbg(dev, "%s - even parity\n", __func__);
34662306a36Sopenharmony_ci		}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci		if (C_CMSPAR(tty)) {
34962306a36Sopenharmony_ci			tty->termios.c_cflag &= ~CMSPAR;
35062306a36Sopenharmony_ci			dev_warn(dev, "MARK/SPACE parity is not supported\n");
35162306a36Sopenharmony_ci		}
35262306a36Sopenharmony_ci	} else {
35362306a36Sopenharmony_ci		request.params |= UPD78F0730_PARITY_NONE;
35462306a36Sopenharmony_ci		dev_dbg(dev, "%s - no parity\n", __func__);
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	if (C_CSTOPB(tty)) {
35862306a36Sopenharmony_ci		request.params |= UPD78F0730_STOP_BIT_2_BIT;
35962306a36Sopenharmony_ci		dev_dbg(dev, "%s - 2 stop bits\n", __func__);
36062306a36Sopenharmony_ci	} else {
36162306a36Sopenharmony_ci		request.params |= UPD78F0730_STOP_BIT_1_BIT;
36262306a36Sopenharmony_ci		dev_dbg(dev, "%s - 1 stop bit\n", __func__);
36362306a36Sopenharmony_ci	}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	if (C_CRTSCTS(tty)) {
36662306a36Sopenharmony_ci		tty->termios.c_cflag &= ~CRTSCTS;
36762306a36Sopenharmony_ci		dev_warn(dev, "RTSCTS flow control is not supported\n");
36862306a36Sopenharmony_ci	}
36962306a36Sopenharmony_ci	if (I_IXOFF(tty) || I_IXON(tty)) {
37062306a36Sopenharmony_ci		tty->termios.c_iflag &= ~(IXOFF | IXON);
37162306a36Sopenharmony_ci		dev_warn(dev, "XON/XOFF flow control is not supported\n");
37262306a36Sopenharmony_ci	}
37362306a36Sopenharmony_ci	request.params |= UPD78F0730_FLOW_CONTROL_NONE;
37462306a36Sopenharmony_ci	dev_dbg(dev, "%s - no flow control\n", __func__);
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	upd78f0730_send_ctl(port, &request, sizeof(request));
37762306a36Sopenharmony_ci}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cistatic int upd78f0730_open(struct tty_struct *tty, struct usb_serial_port *port)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	static const struct upd78f0730_open_close request = {
38262306a36Sopenharmony_ci		.opcode = UPD78F0730_CMD_OPEN_CLOSE,
38362306a36Sopenharmony_ci		.state = UPD78F0730_PORT_OPEN
38462306a36Sopenharmony_ci	};
38562306a36Sopenharmony_ci	int res;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	res = upd78f0730_send_ctl(port, &request, sizeof(request));
38862306a36Sopenharmony_ci	if (res)
38962306a36Sopenharmony_ci		return res;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	if (tty)
39262306a36Sopenharmony_ci		upd78f0730_set_termios(tty, port, NULL);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	return usb_serial_generic_open(tty, port);
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_cistatic void upd78f0730_close(struct usb_serial_port *port)
39862306a36Sopenharmony_ci{
39962306a36Sopenharmony_ci	static const struct upd78f0730_open_close request = {
40062306a36Sopenharmony_ci		.opcode = UPD78F0730_CMD_OPEN_CLOSE,
40162306a36Sopenharmony_ci		.state = UPD78F0730_PORT_CLOSE
40262306a36Sopenharmony_ci	};
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	usb_serial_generic_close(port);
40562306a36Sopenharmony_ci	upd78f0730_send_ctl(port, &request, sizeof(request));
40662306a36Sopenharmony_ci}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_cistatic struct usb_serial_driver upd78f0730_device = {
40962306a36Sopenharmony_ci	.driver	 = {
41062306a36Sopenharmony_ci		.owner	= THIS_MODULE,
41162306a36Sopenharmony_ci		.name	= "upd78f0730",
41262306a36Sopenharmony_ci	},
41362306a36Sopenharmony_ci	.id_table	= id_table,
41462306a36Sopenharmony_ci	.num_ports	= 1,
41562306a36Sopenharmony_ci	.port_probe	= upd78f0730_port_probe,
41662306a36Sopenharmony_ci	.port_remove	= upd78f0730_port_remove,
41762306a36Sopenharmony_ci	.open		= upd78f0730_open,
41862306a36Sopenharmony_ci	.close		= upd78f0730_close,
41962306a36Sopenharmony_ci	.set_termios	= upd78f0730_set_termios,
42062306a36Sopenharmony_ci	.tiocmget	= upd78f0730_tiocmget,
42162306a36Sopenharmony_ci	.tiocmset	= upd78f0730_tiocmset,
42262306a36Sopenharmony_ci	.dtr_rts	= upd78f0730_dtr_rts,
42362306a36Sopenharmony_ci	.break_ctl	= upd78f0730_break_ctl,
42462306a36Sopenharmony_ci};
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_cistatic struct usb_serial_driver * const serial_drivers[] = {
42762306a36Sopenharmony_ci	&upd78f0730_device,
42862306a36Sopenharmony_ci	NULL
42962306a36Sopenharmony_ci};
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_cimodule_usb_serial_driver(serial_drivers, id_table);
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
43462306a36Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR);
43562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
436