* [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* [PATCH V1 2/2] usb: serial: f81534: Implement break control
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 ` 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
1 sibling, 1 reply; 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
Implement Fintek f81534 break on/off with LCR register.
It's the same with 16550A LCR register layout.
Signed-off-by: Ji-Ze Hong (Peter Hong) <hpeter+linux_kernel@gmail.com>
---
drivers/usb/serial/f81534.c | 46 +++++++++++++++++++++++++++++++++++++++------
1 file changed, 40 insertions(+), 6 deletions(-)
diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c
index 3f47afa..9ea407d 100644
--- a/drivers/usb/serial/f81534.c
+++ b/drivers/usb/serial/f81534.c
@@ -128,11 +128,13 @@ struct f81534_serial_private {
struct f81534_port_private {
struct mutex mcr_mutex;
+ struct mutex lcr_mutex;
struct work_struct lsr_work;
struct usb_serial_port *port;
unsigned long tx_empty;
spinlock_t msr_lock;
u8 shadow_mcr;
+ u8 shadow_lcr;
u8 shadow_msr;
u8 phy_num;
};
@@ -465,6 +467,7 @@ static u32 f81534_calc_baud_divisor(u32 baudrate, u32 clockrate)
static int f81534_set_port_config(struct usb_serial_port *port, u32 baudrate,
u8 lcr)
{
+ struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
u32 divisor;
int status;
u8 value;
@@ -493,35 +496,64 @@ static int f81534_set_port_config(struct usb_serial_port *port, u32 baudrate,
}
divisor = f81534_calc_baud_divisor(baudrate, F81534_MAX_BAUDRATE);
+
+ mutex_lock(&port_priv->lcr_mutex);
+
value = UART_LCR_DLAB;
status = f81534_set_port_register(port, F81534_LINE_CONTROL_REG,
value);
if (status) {
dev_err(&port->dev, "%s: set LCR failed\n", __func__);
- return status;
+ goto final;
}
value = divisor & 0xff;
status = f81534_set_port_register(port, F81534_DIVISOR_LSB_REG, value);
if (status) {
dev_err(&port->dev, "%s: set DLAB LSB failed\n", __func__);
- return status;
+ goto final;
}
value = (divisor >> 8) & 0xff;
status = f81534_set_port_register(port, F81534_DIVISOR_MSB_REG, value);
if (status) {
dev_err(&port->dev, "%s: set DLAB MSB failed\n", __func__);
- return status;
+ goto final;
}
- status = f81534_set_port_register(port, F81534_LINE_CONTROL_REG, lcr);
+ value = lcr | (port_priv->shadow_lcr & UART_LCR_SBC);
+ status = f81534_set_port_register(port, F81534_LINE_CONTROL_REG,
+ value);
if (status) {
dev_err(&port->dev, "%s: set LCR failed\n", __func__);
- return status;
+ goto final;
}
- return 0;
+ port_priv->shadow_lcr = value;
+final:
+ mutex_unlock(&port_priv->lcr_mutex);
+ return status;
+}
+
+static void f81534_break_ctl(struct tty_struct *tty, int break_state)
+{
+ struct usb_serial_port *port = tty->driver_data;
+ struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
+ int status;
+
+ mutex_lock(&port_priv->lcr_mutex);
+
+ if (break_state)
+ port_priv->shadow_lcr |= UART_LCR_SBC;
+ else
+ port_priv->shadow_lcr &= ~UART_LCR_SBC;
+
+ status = f81534_set_port_register(port, F81534_LINE_CONTROL_REG,
+ port_priv->shadow_lcr);
+ if (status)
+ dev_err(&port->dev, "set break failed: %x\n", status);
+
+ mutex_unlock(&port_priv->lcr_mutex);
}
static int f81534_update_mctrl(struct usb_serial_port *port, unsigned int set,
@@ -1193,6 +1225,7 @@ static int f81534_port_probe(struct usb_serial_port *port)
spin_lock_init(&port_priv->msr_lock);
mutex_init(&port_priv->mcr_mutex);
+ mutex_init(&port_priv->lcr_mutex);
INIT_WORK(&port_priv->lsr_work, f81534_lsr_worker);
/* Assign logic-to-phy mapping */
@@ -1359,6 +1392,7 @@ static struct usb_serial_driver f81534_device = {
.attach = f81534_attach,
.port_probe = f81534_port_probe,
.port_remove = f81534_port_remove,
+ .break_ctl = f81534_break_ctl,
.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* Re: [PATCH V1 2/2] usb: serial: f81534: Implement break control
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
0 siblings, 0 replies; 4+ messages in thread
From: Johan Hovold @ 2017-10-31 8:59 UTC (permalink / raw)
To: Ji-Ze Hong (Peter Hong)
Cc: johan, gregkh, linux-usb, linux-kernel, hpeter+linux_kernel,
peter_hong
On Fri, Oct 13, 2017 at 10:21:35AM +0800, Ji-Ze Hong (Peter Hong) wrote:
> Implement Fintek f81534 break on/off with LCR register.
> It's the same with 16550A LCR register layout.
>
> Signed-off-by: Ji-Ze Hong (Peter Hong) <hpeter+linux_kernel@gmail.com>
> ---
> drivers/usb/serial/f81534.c | 46 +++++++++++++++++++++++++++++++++++++++------
> 1 file changed, 40 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c
> index 3f47afa..9ea407d 100644
> --- a/drivers/usb/serial/f81534.c
> +++ b/drivers/usb/serial/f81534.c
> @@ -128,11 +128,13 @@ struct f81534_serial_private {
>
> struct f81534_port_private {
> struct mutex mcr_mutex;
> + struct mutex lcr_mutex;
I guess we could have used a single mutex for this, but I kept both.
> struct work_struct lsr_work;
> struct usb_serial_port *port;
> unsigned long tx_empty;
> spinlock_t msr_lock;
> u8 shadow_mcr;
> + u8 shadow_lcr;
> u8 shadow_msr;
> u8 phy_num;
> };
> @@ -493,35 +496,64 @@ static int f81534_set_port_config(struct usb_serial_port *port, u32 baudrate,
> }
>
> divisor = f81534_calc_baud_divisor(baudrate, F81534_MAX_BAUDRATE);
> +
> + mutex_lock(&port_priv->lcr_mutex);
> +
> value = UART_LCR_DLAB;
> status = f81534_set_port_register(port, F81534_LINE_CONTROL_REG,
> value);
> if (status) {
> dev_err(&port->dev, "%s: set LCR failed\n", __func__);
> - return status;
> + goto final;
And I gave this label the more descriptive "out_unlock" name.
[...]
> - return 0;
> + port_priv->shadow_lcr = value;
> +final:
> + mutex_unlock(&port_priv->lcr_mutex);
> + return status;
> +}
> +
> +static void f81534_break_ctl(struct tty_struct *tty, int break_state)
> +{
> + struct usb_serial_port *port = tty->driver_data;
> + struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
> + int status;
> +
> + mutex_lock(&port_priv->lcr_mutex);
> +
> + if (break_state)
> + port_priv->shadow_lcr |= UART_LCR_SBC;
> + else
> + port_priv->shadow_lcr &= ~UART_LCR_SBC;
> +
> + status = f81534_set_port_register(port, F81534_LINE_CONTROL_REG,
> + port_priv->shadow_lcr);
> + if (status)
> + dev_err(&port->dev, "set break failed: %x\n", status);
And fixed the s/%x/%d/ here as well.
> +
> + mutex_unlock(&port_priv->lcr_mutex);
> }
Now applied.
Thanks,
Johan
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH V1 1/2] usb: serial: f81534: fix hang-up on overrun
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:56 ` Johan Hovold
1 sibling, 0 replies; 4+ messages in thread
From: Johan Hovold @ 2017-10-31 8:56 UTC (permalink / raw)
To: Ji-Ze Hong (Peter Hong)
Cc: johan, gregkh, linux-usb, linux-kernel, hpeter+linux_kernel,
peter_hong
On Fri, Oct 13, 2017 at 10:21:34AM +0800, Ji-Ze Hong (Peter Hong) wrote:
> 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>
> +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;
I separated declaration from initialisation here to avoid the line
breaks.
> + 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);
And I changed this to %d before applying as you're printing a negative
errno here.
> +}
> +
Now applied for -next.
Thanks,
Johan
^ permalink raw reply [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