-
Notifications
You must be signed in to change notification settings - Fork 2
/
Linux_4.14.114_ch341.patch
126 lines (116 loc) · 3.43 KB
/
Linux_4.14.114_ch341.patch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c
index 578596d30..fe27c06f3 100644
--- a/drivers/usb/serial/ch341.c
+++ b/drivers/usb/serial/ch341.c
@@ -54,8 +54,7 @@
/*******************************/
/* baudrate calculation factor */
/*******************************/
-#define CH341_BAUDBASE_FACTOR 1532620800
-#define CH341_BAUDBASE_DIVMAX 3
+#define CH341_OSC_F (12000000UL)
/* Break support - the information used to implement this was gleaned from
* the Net/FreeBSD uchcom.c driver by Takanori Watanabe. Domo arigato.
@@ -68,7 +67,10 @@
#define CH341_REQ_MODEM_CTRL 0xA4
#define CH341_REG_BREAK 0x05
+#define CH341_REG_BPS_PRE 0x12
+#define CH341_REG_BPS_DIV 0x13
#define CH341_REG_LCR 0x18
+#define CH341_REG_LCR2 0x25
#define CH341_NBREAK_BITS 0x01
#define CH341_LCR_ENABLE_RX 0x80
@@ -98,6 +100,28 @@ struct ch341_private {
u8 lcr;
};
+struct ch341_prescalers {
+ u8 reg_value;
+ u32 prescaler_div;
+};
+
+/*
+ * CH341A has 3 chained prescalers
+ * bit 0: =1: disable prescaler factor *8
+ * bit 1: =1: disable prescaler factor *64
+ * bit 2: =1: disable prescaler factor *2
+ */
+static const struct ch341_prescalers scaler_tab[] = {
+ { 7, 1 },
+ { 3, 2 },
+ { 6, 8 },
+ { 2, 16 },
+ { 5, 64 },
+ { 1, 128 },
+ { 4, 512 },
+ { 0, 1024 }
+};
+
static void ch341_set_termios(struct tty_struct *tty,
struct usb_serial_port *port,
struct ktermios *old_termios);
@@ -150,38 +174,54 @@ static int ch341_control_in(struct usb_device *dev,
static int ch341_set_baudrate_lcr(struct usb_device *dev,
struct ch341_private *priv, u8 lcr)
{
- short a;
+ int found_div;
+ u8 div_regvalue;
+ u8 prescaler_regvalue;
+ short prescaler_index;
int r;
- unsigned long factor;
- short divisor;
- if (!priv->baud_rate)
+ if (priv->baud_rate < 46 || priv->baud_rate > 3030000)
return -EINVAL;
- factor = (CH341_BAUDBASE_FACTOR / priv->baud_rate);
- divisor = CH341_BAUDBASE_DIVMAX;
- while ((factor > 0xfff0) && divisor) {
- factor >>= 3;
- divisor--;
+ found_div = 0;
+ // start with the smallest possible prescaler value to get the
+ // best precision at first match (largest mantissa value)
+ for (prescaler_index = 0; prescaler_index < ARRAY_SIZE(scaler_tab);
+ ++prescaler_index) {
+ unsigned long prescaler;
+ unsigned long div;
+
+ prescaler = scaler_tab[prescaler_index].prescaler_div;
+ div = ((2UL * CH341_OSC_F)
+ / (prescaler * priv->baud_rate) + 1UL) / 2UL;
+ // when prescaler==1 the divisors from 8 to 2 are
+ // actually 16 to 4, skip them
+ if (prescaler == 1 && div <= 8) {
+ continue;
+ } else if (div <= 256 && div >= 2) {
+ found_div = 1;
+ prescaler_regvalue =
+ scaler_tab[prescaler_index].reg_value | BIT(7);
+ div_regvalue = 256 - div;
+ break;
+ }
}
- if (factor > 0xfff0)
+ if (!found_div)
return -EINVAL;
- factor = 0x10000 - factor;
- a = (factor & 0xff00) | divisor;
-
/*
* CH341A buffers data until a full endpoint-size packet (32 bytes)
* has been received unless bit 7 is set.
*/
- a |= BIT(7);
-
- r = ch341_control_out(dev, CH341_REQ_WRITE_REG, 0x1312, a);
+ r = ch341_control_out(dev, CH341_REQ_WRITE_REG,
+ (CH341_REG_BPS_DIV << 8) | CH341_REG_BPS_PRE,
+ (div_regvalue << 8) | prescaler_regvalue);
if (r)
return r;
- r = ch341_control_out(dev, CH341_REQ_WRITE_REG, 0x2518, lcr);
+ r = ch341_control_out(dev, CH341_REQ_WRITE_REG,
+ (CH341_REG_LCR2 << 8) | CH341_REG_LCR, lcr);
if (r)
return r;