USB: serial: ch341: reimplement line-speed handling
authorJohan Hovold <johan@kernel.org>
Fri, 1 Nov 2019 17:24:10 +0000 (18:24 +0100)
committerJohan Hovold <johan@kernel.org>
Mon, 4 Nov 2019 11:49:51 +0000 (12:49 +0100)
commit35714565089e5e8b091c1155517b67e29118f09d
treee8ef260a6bdc347e6888f776a781c566e7a9f4c6
parentebd09f1cd417fce9c85de3abcabf51eadf907a2a
USB: serial: ch341: reimplement line-speed handling

The current ch341 divisor algorithm was known to give inaccurate results
for certain higher line speeds. Jonathan Olds <jontio@i4free.co.nz>
investigated this, determined the basic equations used to derive the
divisors and confirmed them experimentally [1].

The equations Jonathan used could be generalised further to:

baudrate = 48000000 / (2^(12 - 3 * ps - fact) * div), where

0 <= ps <= 3,
0 <= fact <= 1,
2 <= div <= 256 if fact = 0, or
9 <= div <= 256 if fact = 1

which will also give better results for lower rates.

Notably the error is reduced for the following standard rates:

1152000 (4.0% instead of 15% error)
 921600 (0.16% instead of -7.5% error)
 576000 (-0.80% instead of -5.6% error)
    200 (0.16% instead of -0.69% error)
    134 (-0.05% instead of -0.63% error)
    110 (0.03% instead of -0.44% error)

but also for many non-standard ones.

The current algorithm also suffered from rounding issues (e.g.
requesting 2950000 Bd resulted in a rate of 2 MBd instead of 3 MBd and
thus a -32% instead of 1.7% error).

The new algorithm was inspired by the current vendor driver even if that
one only handles two higher rates that require fact=1 by hard coding the
corresponding divisors [2].

Michael Dreher <michael@5dot1.de> also did a similar generalisation of
Jonathan's work and has published his results with a very good summary
that provides further insights into how this device works [3].

[1] https://lkml.kernel.org/r/000001d51f34$bad6afd0$30840f70$@co.nz
[2] http://www.wch.cn/download/CH341SER_LINUX_ZIP.html
[3] https://github.com/nospam2000/ch341-baudrate-calculation

Reported-by: Jonathan Olds <jontio@i4free.co.nz>
Tested-by: Jonathan Olds <jontio@i4free.co.nz>
Cc: Michael Dreher <michael@5dot1.de>
Signed-off-by: Johan Hovold <johan@kernel.org>
drivers/usb/serial/ch341.c