public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH V1 1/2] usb: serial: f81534: fix hang-up on overrun
@ 2017-10-13  2:21 Ji-Ze Hong (Peter Hong)
  2017-10-13  2:21 ` [PATCH V1 2/2] usb: serial: f81534: Implement break control Ji-Ze Hong (Peter Hong)
  2017-10-31  8:56 ` [PATCH V1 1/2] usb: serial: f81534: fix hang-up on overrun Johan Hovold
  0 siblings, 2 replies; 4+ messages in thread
From: Ji-Ze Hong (Peter Hong) @ 2017-10-13  2:21 UTC (permalink / raw)
  To: johan; +Cc: gregkh, linux-usb, linux-kernel, hpeter+linux_kernel, peter_hong

The F81532/534 without this patch will hang-up on data overrun.

It's caused by enable LSR interrupt in IER by default and occur data
overrun, the chip will busy for process LSR interrupt but not read LSR
internally. It will not responed for USB control endpoint0 and we can't
read LSR from driver in this situration.

So we'll disable the LSR interrupt in probe() and submit the LSR worker to
clear LSR state when reported LSR error bit with bulk-in data in
f81534_process_per_serial_block().

Signed-off-by: Ji-Ze Hong (Peter Hong) <hpeter+linux_kernel@gmail.com>
---
 drivers/usb/serial/f81534.c | 42 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c
index 3d616a2..3f47afa 100644
--- a/drivers/usb/serial/f81534.c
+++ b/drivers/usb/serial/f81534.c
@@ -39,9 +39,11 @@
 #define F81534_UART_OFFSET		0x10
 #define F81534_DIVISOR_LSB_REG		(0x00 + F81534_UART_BASE_ADDRESS)
 #define F81534_DIVISOR_MSB_REG		(0x01 + F81534_UART_BASE_ADDRESS)
+#define F81534_INTERRUPT_ENABLE_REG	(0x01 + F81534_UART_BASE_ADDRESS)
 #define F81534_FIFO_CONTROL_REG		(0x02 + F81534_UART_BASE_ADDRESS)
 #define F81534_LINE_CONTROL_REG		(0x03 + F81534_UART_BASE_ADDRESS)
 #define F81534_MODEM_CONTROL_REG	(0x04 + F81534_UART_BASE_ADDRESS)
+#define F81534_LINE_STATUS_REG		(0x05 + F81534_UART_BASE_ADDRESS)
 #define F81534_MODEM_STATUS_REG		(0x06 + F81534_UART_BASE_ADDRESS)
 #define F81534_CONFIG1_REG		(0x09 + F81534_UART_BASE_ADDRESS)
 
@@ -126,6 +128,8 @@ struct f81534_serial_private {
 
 struct f81534_port_private {
 	struct mutex mcr_mutex;
+	struct work_struct lsr_work;
+	struct usb_serial_port *port;
 	unsigned long tx_empty;
 	spinlock_t msr_lock;
 	u8 shadow_mcr;
@@ -1015,6 +1019,8 @@ static void f81534_process_per_serial_block(struct usb_serial_port *port,
 				tty_insert_flip_char(&port->port, 0,
 						TTY_OVERRUN);
 			}
+
+			schedule_work(&port_priv->lsr_work);
 		}
 
 		if (port->port.console && port->sysrq) {
@@ -1162,6 +1168,20 @@ static int f81534_attach(struct usb_serial *serial)
 	return 0;
 }
 
+static void f81534_lsr_worker(struct work_struct *work)
+{
+	struct f81534_port_private *port_priv =
+			container_of(work, struct f81534_port_private,
+					lsr_work);
+	struct usb_serial_port *port = port_priv->port;
+	int status;
+	u8 tmp;
+
+	status = f81534_get_port_register(port, F81534_LINE_STATUS_REG, &tmp);
+	if (status)
+		dev_warn(&port->dev, "read LSR failed: %x\n", status);
+}
+
 static int f81534_port_probe(struct usb_serial_port *port)
 {
 	struct f81534_port_private *port_priv;
@@ -1173,6 +1193,7 @@ static int f81534_port_probe(struct usb_serial_port *port)
 
 	spin_lock_init(&port_priv->msr_lock);
 	mutex_init(&port_priv->mcr_mutex);
+	INIT_WORK(&port_priv->lsr_work, f81534_lsr_worker);
 
 	/* Assign logic-to-phy mapping */
 	ret = f81534_logic_to_phy_port(port->serial, port);
@@ -1180,10 +1201,30 @@ static int f81534_port_probe(struct usb_serial_port *port)
 		return ret;
 
 	port_priv->phy_num = ret;
+	port_priv->port = port;
 	usb_set_serial_port_data(port, port_priv);
 	dev_dbg(&port->dev, "%s: port_number: %d, phy_num: %d\n", __func__,
 			port->port_number, port_priv->phy_num);
 
+	/*
+	 * The F81532/534 will hang-up when enable LSR interrupt in IER and
+	 * occur data overrun. So we'll disable the LSR interrupt in probe()
+	 * and submit the LSR worker to clear LSR state when reported LSR error
+	 * bit with bulk-in data in f81534_process_per_serial_block().
+	 */
+	ret = f81534_set_port_register(port, F81534_INTERRUPT_ENABLE_REG,
+			UART_IER_RDI | UART_IER_THRI | UART_IER_MSI);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int f81534_port_remove(struct usb_serial_port *port)
+{
+	struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
+
+	flush_work(&port_priv->lsr_work);
 	return 0;
 }
 
@@ -1317,6 +1358,7 @@ static struct usb_serial_driver f81534_device = {
 	.calc_num_ports =	f81534_calc_num_ports,
 	.attach =		f81534_attach,
 	.port_probe =		f81534_port_probe,
+	.port_remove =		f81534_port_remove,
 	.dtr_rts =		f81534_dtr_rts,
 	.process_read_urb =	f81534_process_read_urb,
 	.ioctl =		f81534_ioctl,
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2017-10-31  8:59 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-10-13  2:21 [PATCH V1 1/2] usb: serial: f81534: fix hang-up on overrun Ji-Ze Hong (Peter Hong)
2017-10-13  2:21 ` [PATCH V1 2/2] usb: serial: f81534: Implement break control Ji-Ze Hong (Peter Hong)
2017-10-31  8:59   ` Johan Hovold
2017-10-31  8:56 ` [PATCH V1 1/2] usb: serial: f81534: fix hang-up on overrun Johan Hovold

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox