linux-usb.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Michael Hanselmann <public@hansmi.ch>
To: linux-usb@vger.kernel.org, Johan Hovold <johan@kernel.org>
Cc: Michael Hanselmann <public@hansmi.ch>,
	Michael Dreher <michael@5dot1.de>,
	Jonathan Olds <jontio@i4free.co.nz>
Subject: [PATCH 3/4] ch341: Limit prescaler on HL340 variant
Date: Fri,  6 Mar 2020 19:00:44 +0000	[thread overview]
Message-ID: <d76985a6dcf1b4aeec783dd8c8b59f054b51e07d.1583520568.git.public@hansmi.ch> (raw)
In-Reply-To: <cover.1583520568.git.public@hansmi.ch>

HL340 devices, a subset of all CH340 devices, do not work correctly when
the highest prescaler bit (0b100) is set. Limit these to the lower
prescaler values at the cost of timing precision.

Signed-off-by: Michael Hanselmann <public@hansmi.ch>
---
 drivers/usb/serial/ch341.c | 53 ++++++++++++++++++++++++++++----------
 1 file changed, 40 insertions(+), 13 deletions(-)

diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c
index 0523f46f53c7..48a704174aec 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)
@@ -143,13 +145,19 @@ static int ch341_control_in(struct usb_device *dev,
 
 #define CH341_CLKRATE		48000000
 #define CH341_CLK_DIV(ps, fact)	(1 << (12 - 3 * (ps) - (fact)))
-#define CH341_MIN_RATE(ps)	(CH341_CLKRATE / (CH341_CLK_DIV((ps), 1) * 512))
+#define CH341_MIN_RATE(ps, fact) \
+	(CH341_CLKRATE / (CH341_CLK_DIV((ps), (fact)) * 512))
 
 static const speed_t ch341_min_rates[] = {
-	CH341_MIN_RATE(0),
-	CH341_MIN_RATE(1),
-	CH341_MIN_RATE(2),
-	CH341_MIN_RATE(3),
+	CH341_MIN_RATE(0, 0),
+	CH341_MIN_RATE(1, 0),
+	CH341_MIN_RATE(2, 0),
+	CH341_MIN_RATE(3, 0),
+
+	CH341_MIN_RATE(0, 1),
+	CH341_MIN_RATE(1, 1),
+	CH341_MIN_RATE(2, 1),
+	CH341_MIN_RATE(3, 1),
 };
 
 /*
@@ -162,24 +170,41 @@ 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)
 {
+	const speed_t *min_rates;
+	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 (fact = 1) that will give a
-	 * divisor strictly less than 512.
-	 */
-	fact = 1;
+	if (priv->flags & CH341_QUIRK_LIMITED_PRESCALER) {
+		/*
+		 * Devices of the "HL340" variant don't work reliably when the
+		 * third bit is set in the prescaler (0b100). Limit these to
+		 * prescaler values in the range 0..3 (fact = 0) at the cost of
+		 * precision.
+		 */
+		min_rates = &ch341_min_rates[4];
+		fact = 0;
+	} else {
+		/*
+		 * Start with highest possible base clock (fact = 1) that will
+		 * give a divisor strictly less than 512.
+		 */
+		min_rates = ch341_min_rates;
+		fact = 1;
+	}
+
 	for (ps = 3; ps >= 0; ps--) {
-		if (speed > ch341_min_rates[ps])
+		if (speed > min_rates[ps])
 			break;
 	}
 
@@ -231,7 +256,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;
 
@@ -377,6 +402,8 @@ static int ch341_port_probe(struct usb_serial_port *port)
 	r = ch341_detect_hl340(port->serial->dev);
 	if (r < 0)
 		goto error;
+	else if (r != 0)
+		priv->flags |= CH341_QUIRK_LIMITED_PRESCALER;
 
 	usb_set_serial_port_data(port, priv);
 	return 0;
-- 
2.20.1


  parent reply	other threads:[~2020-03-06 19:00 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-03-06 19:00 [PATCH 0/4] ch341: Add support for HL340 devices Michael Hanselmann
2020-03-06 19:00 ` [PATCH 1/4] ch341: Name more registers Michael Hanselmann
2020-03-24 10:20   ` Johan Hovold
2020-03-31 23:34     ` Michael Hanselmann
2020-03-06 19:00 ` [PATCH 2/4] ch341: Detect HL340 variant Michael Hanselmann
2020-03-24 10:31   ` Johan Hovold
2020-03-31 23:35     ` Michael Hanselmann
2020-03-06 19:00 ` Michael Hanselmann [this message]
2020-03-24 10:41   ` [PATCH 3/4] ch341: Limit prescaler on " Johan Hovold
2020-03-31 23:35     ` Michael Hanselmann
2020-03-06 19:00 ` [PATCH 4/4] ch341: Simulate break condition " Michael Hanselmann
2020-03-24 10:55   ` Johan Hovold
2020-03-24 10:05 ` [PATCH 0/4] ch341: Add support for HL340 devices Johan Hovold
2020-03-31 23:35   ` Michael Hanselmann

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=d76985a6dcf1b4aeec783dd8c8b59f054b51e07d.1583520568.git.public@hansmi.ch \
    --to=public@hansmi.ch \
    --cc=johan@kernel.org \
    --cc=jontio@i4free.co.nz \
    --cc=linux-usb@vger.kernel.org \
    --cc=michael@5dot1.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).