Commit 764de105 authored by Michael G. Katzmann's avatar Michael G. Katzmann Committed by Johan Hovold

USB: serial: pl2303: TA & TB alternate divider with non-standard baud rates

Use an alternate clock divider algorithm and bit ordering for the TA and
TB versions of the pl2303. It was discovered that these variants do not
produce the correct baud rates with the existing scheme.

see https://lore.kernel.org/r/3aee5708-7961-f464-8c5f-6685d96920d6@IEEE.orgSigned-off-by: default avatarMichael G. Katzmann <michaelk@IEEE.org>
Signed-off-by: default avatarJohan Hovold <johan@kernel.org>
parent 8cbc7539
...@@ -188,6 +188,7 @@ struct pl2303_type_data { ...@@ -188,6 +188,7 @@ struct pl2303_type_data {
unsigned long quirks; unsigned long quirks;
unsigned int no_autoxonxoff:1; unsigned int no_autoxonxoff:1;
unsigned int no_divisors:1; unsigned int no_divisors:1;
unsigned int alt_divisors:1;
}; };
struct pl2303_serial_private { struct pl2303_serial_private {
...@@ -217,10 +218,12 @@ static const struct pl2303_type_data pl2303_type_data[TYPE_COUNT] = { ...@@ -217,10 +218,12 @@ static const struct pl2303_type_data pl2303_type_data[TYPE_COUNT] = {
[TYPE_TA] = { [TYPE_TA] = {
.name = "TA", .name = "TA",
.max_baud_rate = 6000000, .max_baud_rate = 6000000,
.alt_divisors = true,
}, },
[TYPE_TB] = { [TYPE_TB] = {
.name = "TB", .name = "TB",
.max_baud_rate = 12000000, .max_baud_rate = 12000000,
.alt_divisors = true,
}, },
[TYPE_HXD] = { [TYPE_HXD] = {
.name = "HXD", .name = "HXD",
...@@ -618,6 +621,45 @@ static speed_t pl2303_encode_baud_rate_divisor(unsigned char buf[4], ...@@ -618,6 +621,45 @@ static speed_t pl2303_encode_baud_rate_divisor(unsigned char buf[4],
return baud; return baud;
} }
static speed_t pl2303_encode_baud_rate_divisor_alt(unsigned char buf[4],
speed_t baud)
{
unsigned int baseline, mantissa, exponent;
/*
* Apparently, for the TA version the formula is:
* baudrate = 12M * 32 / (mantissa * 2^exponent)
* where
* mantissa = buf[10:0]
* exponent = buf[15:13 16]
*/
baseline = 12000000 * 32;
mantissa = baseline / baud;
if (mantissa == 0)
mantissa = 1; /* Avoid dividing by zero if baud > 32*12M. */
exponent = 0;
while (mantissa >= 2048) {
if (exponent < 15) {
mantissa >>= 1; /* divide by 2 */
exponent++;
} else {
/* Exponent is maxed. Trim mantissa and leave. */
mantissa = 2047;
break;
}
}
buf[3] = 0x80;
buf[2] = exponent & 0x01;
buf[1] = (exponent & ~0x01) << 4 | mantissa >> 8;
buf[0] = mantissa & 0xff;
/* Calculate and return the exact baud rate. */
baud = (baseline / mantissa) >> exponent;
return baud;
}
static void pl2303_encode_baud_rate(struct tty_struct *tty, static void pl2303_encode_baud_rate(struct tty_struct *tty,
struct usb_serial_port *port, struct usb_serial_port *port,
u8 buf[4]) u8 buf[4])
...@@ -645,6 +687,8 @@ static void pl2303_encode_baud_rate(struct tty_struct *tty, ...@@ -645,6 +687,8 @@ static void pl2303_encode_baud_rate(struct tty_struct *tty,
if (baud == baud_sup) if (baud == baud_sup)
baud = pl2303_encode_baud_rate_direct(buf, baud); baud = pl2303_encode_baud_rate_direct(buf, baud);
else if (spriv->type->alt_divisors)
baud = pl2303_encode_baud_rate_divisor_alt(buf, baud);
else else
baud = pl2303_encode_baud_rate_divisor(buf, baud); baud = pl2303_encode_baud_rate_divisor(buf, baud);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment