0dd1cdd4360904d1307c0f65ec616821aca241dd
[openwrt/staging/blocktrron.git] /
1 From 6952209ef220138189dd261d06441e1b2d50e994 Mon Sep 17 00:00:00 2001
2 From: Weijie Gao <weijie.gao@mediatek.com>
3 Date: Fri, 23 May 2025 17:26:02 +0800
4 Subject: [PATCH] serial: mediatek: enable baudrate accuracy compensation
5
6 The high-speed UART from MediaTek supports baudrate accuracy
7 compensation when using high-speed mode 3.
8
9 This is done by calculating the first digit of the fraction part of
10 sample count value. The fraction value will be then used as the
11 reference to insert 0 to 10 sample cycle(s) to one frame (assume
12 that frame format is 8n1, i.e. 10 bits per frame).
13
14 The fracdiv_[l/m] registers are used to determine whether a bit in one frame
15 should be inserted with one sample cycle.
16
17 With typical 40MHz source clock, the actual baudrates with/without
18 accuracy compensation are:
19
20 Ideal w/o compensation w/ compensation
21 ======== ================ ===============
22 9600 9603 9600
23 115200 114942 115207
24 921600 930232 921659
25 3000000 3076923 3007519
26
27 Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
28 ---
29 drivers/serial/serial_mtk.c | 24 +++++++++++++++++++++---
30 1 file changed, 21 insertions(+), 3 deletions(-)
31
32 --- a/drivers/serial/serial_mtk.c
33 +++ b/drivers/serial/serial_mtk.c
34 @@ -99,10 +99,18 @@ struct mtk_serial_priv {
35 bool upstream_highspeed_logic;
36 };
37
38 +static const unsigned short fraction_l_mapping[] = {
39 + 0, 1, 0x5, 0x15, 0x55, 0x57, 0x57, 0x77, 0x7F, 0xFF, 0xFF
40 +};
41 +
42 +static const unsigned short fraction_m_mapping[] = {
43 + 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 3
44 +};
45 +
46 static void _mtk_serial_setbrg(struct mtk_serial_priv *priv, int baud,
47 uint clk_rate)
48 {
49 - u32 quot, realbaud, samplecount = 1;
50 + u32 quot, realbaud, samplecount = 1, fraction, frac_l = 0, frac_m = 0;
51
52 /* Special case for low baud clock */
53 if (baud <= 115200 && clk_rate == 12000000) {
54 @@ -147,7 +155,13 @@ use_hs3:
55 writel(3, &priv->regs->highspeed);
56
57 quot = DIV_ROUND_UP(clk_rate, 256 * baud);
58 - samplecount = DIV_ROUND_CLOSEST(clk_rate, quot * baud);
59 + samplecount = clk_rate / (quot * baud);
60 +
61 + fraction = ((clk_rate * 100) / quot / baud) % 100;
62 + fraction = DIV_ROUND_CLOSEST(fraction, 10);
63 +
64 + frac_l = fraction_l_mapping[fraction];
65 + frac_m = fraction_m_mapping[fraction];
66 }
67
68 set_baud:
69 @@ -159,7 +173,11 @@ set_baud:
70
71 /* set highspeed mode sample count & point */
72 writel(samplecount - 1, &priv->regs->sample_count);
73 - writel((samplecount - 2) >> 1, &priv->regs->sample_point);
74 + writel((samplecount >> 1) - 1, &priv->regs->sample_point);
75 +
76 + /* set baudrate fraction compensation */
77 + writel(frac_l, &priv->regs->fracdiv_l);
78 + writel(frac_m, &priv->regs->fracdiv_m);
79 }
80
81 static int _mtk_serial_putc(struct mtk_serial_priv *priv, const char ch)