Re: [PATCH v2 3/6] USB: serial: ch341: Limit prescaler on quirky chips

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, Mar 31, 2020 at 11:37:19PM +0000, Michael Hanselmann wrote:
> A subset of all CH341 devices stop responding to bulk transfers, usually
> after the third byte, when the highest prescaler bit (0b100) is set.
> There is one exception, namely a prescaler of exactly 0b111 (fact=1,
> ps=3). Limit these to the lower prescaler values at the cost of timing
> precision.
> 
> Rates above 46875 baud use the same timings as the normal algorithm.
> Below that the worst difference between desired and actual baud rate is
> 2.17 percentage points. The worst difference is 1.06 p.p. when only
> looking at divisors differing from the normal algorithm.
> 
> Signed-off-by: Michael Hanselmann <public@xxxxxxxxx>
> ---
>  drivers/usb/serial/ch341.c | 70 ++++++++++++++++++++++++++++----------
>  1 file changed, 52 insertions(+), 18 deletions(-)
> 
> diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c
> index 9c839f67c3d4..67a5d4c3df42 100644
> --- a/drivers/usb/serial/ch341.c
> +++ b/drivers/usb/serial/ch341.c
> @@ -24,6 +24,8 @@
>  #define DEFAULT_BAUD_RATE 9600
>  #define DEFAULT_TIMEOUT   1000
>  
> +#define CH341_QUIRK_LIMITED_PRESCALER 0x01
> +
>  /* flags for IO-Bits */
>  #define CH341_BIT_RTS (1 << 6)
>  #define CH341_BIT_DTR (1 << 5)
> @@ -195,35 +197,67 @@ static int ch341_calc_divisor(speed_t speed, unsigned int ps,
>   *		2 <= div <= 256 if fact = 0, or
>   *		9 <= div <= 256 if fact = 1
>   */
> -static int ch341_get_divisor(speed_t speed)
> +static int ch341_get_divisor(struct ch341_private *priv)
>  {
> +	speed_t speed;
>  	unsigned int fact, div, clk_div;
>  	int ps;
>  
> +	speed = priv->baud_rate;
> +
>  	/*
>  	 * Clamp to supported range, this makes the (ps < 0) and (div < 2)
>  	 * sanity checks below redundant.
>  	 */
>  	speed = clamp(speed, 46U, 3000000U);
>  
> -	/*
> -	 * Start with highest possible base clock and find a divisor for the
> -	 * requested baud rate.
> -	 */
> -	for (ps = 3; ps >= 0; --ps) {
> -		if (ch341_calc_divisor(speed, ps, 1U, &div, &clk_div) == 0) {
> -			fact = 1;
> -			break;
> -		}
> -
> +	if (priv->quirks & CH341_QUIRK_LIMITED_PRESCALER) {
>  		/*
> -		 * Prefer half base clock (fact = 0) before trying lower
> -		 * prescaler values. This makes the receiver more tolerant to
> -		 * errors.
> +		 * A subset of all CH34x devices stop responding to bulk
> +		 * transfers when configured with certain prescaler values.
> +		 *
> +		 * fact=0, ps=0..3: Works
> +		 * fact=1, ps=0..2: Unreliable
> +		 * fact=1, ps=3: Works
> +		 *
> +		 * Limit these devices to working prescaler values at the cost
> +		 * of precision for speeds up to 46875 baud above which
> +		 * fact = 1 with ps = 3 is used.
>  		 */
> -		if (ch341_calc_divisor(speed, ps, 0U, &div, &clk_div) == 0) {
> +		if (ch341_calc_divisor(speed, 3U, 1U, &div, &clk_div) == 0) {
> +			ps = 3U;
> +			fact = 1U;
> +		} else {
>  			fact = 0;
> -			break;
> +
> +			for (ps = 3; ps >= 0; --ps) {
> +				if (ch341_calc_divisor(speed, ps, fact,
> +						       &div, &clk_div) == 0)
> +					break;
> +			}
> +		}
> +	} else {
> +		/*
> +		 * Start with highest possible base clock and find a divisor
> +		 * for the requested baud rate.
> +		 */
> +		for (ps = 3; ps >= 0; --ps) {
> +			if (ch341_calc_divisor(speed, ps, 1U,
> +					       &div, &clk_div) == 0) {
> +				fact = 1;
> +				break;
> +			}
> +
> +			/*
> +			 * Prefer half base clock (fact = 0) before trying
> +			 * lower prescaler values. This makes the receiver more
> +			 * tolerant to errors.
> +			 */
> +			if (ch341_calc_divisor(speed, ps, 0U,
> +					       &div, &clk_div) == 0) {
> +				fact = 0;
> +				break;
> +			}
>  		}
>  	}
>  
> @@ -250,7 +284,7 @@ static int ch341_set_baudrate_lcr(struct usb_device *dev,
>  	if (!priv->baud_rate)
>  		return -EINVAL;
>  
> -	val = ch341_get_divisor(priv->baud_rate);
> +	val = ch341_get_divisor(priv);
>  	if (val < 0)
>  		return -EINVAL;
>  
> @@ -353,7 +387,7 @@ static int ch341_detect_quirks(struct usb_device *dev)
>  	if (r == -EPIPE) {
>  		dev_dbg(&dev->dev, "%s - reading break condition register"
>  			" failed (%d)\n", __func__, r);
> -		r = 0;
> +		r = CH341_QUIRK_LIMITED_PRESCALER;
>  		goto out;
>  	}

This can be implemented in a more compact way which makes the algorithm
easier to follow and doesn't depend on your patch 1/6 either. With a
smaller fix we can get this backported to stable as well.

I've verified that the below works here on top of your (slightly
modified) 2/6. Would you mind giving it a try on your end as well?

Johan


>From ea23730616b6406101d4efeb12e1ae2312dd20c9 Mon Sep 17 00:00:00 2001
From: Johan Hovold <johan@xxxxxxxxxx>
Date: Thu, 14 May 2020 11:36:45 +0200
Subject: [PATCH] USB: serial: ch341: fix lockup of devices with limited
 prescaler

Michael Hanselmann reports that

	[a] subset of all CH341 devices stop responding to bulk
	transfers, usually after the third byte, when the highest
	prescaler bit (0b100) is set. There is one exception, namely a
	prescaler of exactly 0b111 (fact=1, ps=3).

Fix this by forcing a lower base clock (fact = 0) whenever needed.

This specifically makes the standard rates 110, 134 and 200 bps work
again with these devices.

Fixes: 35714565089e ("USB: serial: ch341: reimplement line-speed handling")
Cc: stable <stable@xxxxxxxxxxxxxxx>	# 5.5
Reported-by: Michael Hanselmann <public@xxxxxxxxx>
Signed-off-by: Johan Hovold <johan@xxxxxxxxxx>
---
 drivers/usb/serial/ch341.c | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c
index f2b93f4554a7..89675ee29645 100644
--- a/drivers/usb/serial/ch341.c
+++ b/drivers/usb/serial/ch341.c
@@ -73,6 +73,8 @@
 #define CH341_LCR_CS6          0x01
 #define CH341_LCR_CS5          0x00
 
+#define CH341_QUIRK_LIMITED_PRESCALER	BIT(0)
+
 static const struct usb_device_id id_table[] = {
 	{ USB_DEVICE(0x4348, 0x5523) },
 	{ USB_DEVICE(0x1a86, 0x7523) },
@@ -160,9 +162,11 @@ static const speed_t ch341_min_rates[] = {
  *		2 <= div <= 256 if fact = 0, or
  *		9 <= div <= 256 if fact = 1
  */
-static int ch341_get_divisor(speed_t speed)
+static int ch341_get_divisor(struct ch341_private *priv)
 {
 	unsigned int fact, div, clk_div;
+	speed_t speed = priv->baud_rate;
+	bool force_fact0 = false;
 	int ps;
 
 	/*
@@ -188,8 +192,12 @@ static int ch341_get_divisor(speed_t speed)
 	clk_div = CH341_CLK_DIV(ps, fact);
 	div = CH341_CLKRATE / (clk_div * speed);
 
+	/* Some devices require a lower base clock if ps < 3. */
+	if (ps < 3 && (priv->quirks & CH341_QUIRK_LIMITED_PRESCALER))
+		force_fact0 = true;
+
 	/* Halve base clock (fact = 0) if required. */
-	if (div < 9 || div > 255) {
+	if (div < 9 || div > 255 || force_fact0) {
 		div /= 2;
 		clk_div *= 2;
 		fact = 0;
@@ -228,7 +236,7 @@ static int ch341_set_baudrate_lcr(struct usb_device *dev,
 	if (!priv->baud_rate)
 		return -EINVAL;
 
-	val = ch341_get_divisor(priv->baud_rate);
+	val = ch341_get_divisor(priv);
 	if (val < 0)
 		return -EINVAL;
 
@@ -333,6 +341,7 @@ static int ch341_detect_quirks(struct usb_serial_port *port)
 			    CH341_REG_BREAK, 0, buffer, size, DEFAULT_TIMEOUT);
 	if (r == -EPIPE) {
 		dev_dbg(&port->dev, "break control not supported\n");
+		quirks = CH341_QUIRK_LIMITED_PRESCALER;
 		r = 0;
 		goto out;
 	}
-- 
2.26.2





[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux