[PATCH 3/4] ch341: Limit prescaler on HL340 variant

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

 



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@xxxxxxxxx>
---
 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




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

  Powered by Linux