在测试接收一个 1Mbps 串口数据的驱动上,RK3506 平台有时候启动后会有报错:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
[   23.781504] ttyS4: maybe rx pin is low or baudrate is not correct!
[   23.783156] ttyS4: Frame error!
[   23.783175] ttyS4: maybe rx pin is low or baudrate is not correct!
[   23.785058] ttyS4: Frame error!
[   23.785077] ttyS4: maybe rx pin is low or baudrate is not correct!
[   23.786474] ttyS4: Frame error!
[   23.786483] ttyS4: maybe rx pin is low or baudrate is not correct!
[   23.788138] ttyS4: Frame error!
[   23.788146] ttyS4: maybe rx pin is low or baudrate is not correct!
[   23.789813] ttyS4: Frame error!
[   23.789825] ttyS4: maybe rx pin is low or baudrate is not correct!
[   23.791473] ttyS4: Frame error!
[   23.791483] ttyS4: maybe rx pin is low or baudrate is not correct!
[   23.793137] ttyS4: Frame error!

但这个错误出现几率不高,一旦出现会一直持续报错,断电重启后较大几率会消失。

所以高度怀疑是高波特率通信的电气兼容问题,通过查看内部 UART 时钟发现:

1
2
3
4
5
6
7
8
$ sudo cat /sys/kernel/debug/clk/sclk_uart1/clk_rate
24000000
$ sudo cat /sys/kernel/debug/clk/sclk_uart2/clk_rate
24000000
$ sudo cat /sys/kernel/debug/clk/sclk_uart3/clk_rate
24000000
$ sudo cat /sys/kernel/debug/clk/sclk_uart4/clk_rate
16000000

串口 uart1-3 都使用的115200波特率,时钟频率比使用1M波特率的 uart4 更高,这就奇怪了, 理论上通信波特率越高,时钟频率应该更高。

而且 uart 是16倍过采样,这种情况下,16M 时钟对于 1Mbps 的通信而言,没有任何余量, 任何电气上的影响延迟都会影响通信。所以怀疑是这个地方的问题。

源码文件在 drivers/tty/serial/8250/8250_dw.c中:

 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
static void dw8250_set_termios(struct uart_port *p, struct ktermios *termios,
			       struct ktermios *old)
{
#ifndef CONFIG_ARCH_ROCKCHIP
	unsigned long newrate = tty_termios_baud_rate(termios) * 16;
#endif
	struct dw8250_data *d = to_dw8250_data(p->private_data);
	long rate;
#ifdef CONFIG_ARCH_ROCKCHIP
	unsigned int baud = tty_termios_baud_rate(termios);
	unsigned int rate_temp, diff;
#endif
	int ret;

	clk_disable_unprepare(d->clk);
#ifdef CONFIG_ARCH_ROCKCHIP
	if (d->clk) {
		if (baud <= 115200)
			rate = 24000000;
		else if (baud == 230400)
			rate = baud * 16 * 2;
		else if (baud == 1152000)
			rate = baud * 16 * 2;
		else
			rate = baud * 16;

		ret = clk_set_rate(d->clk, rate);
		rate_temp = clk_get_rate(d->clk);
		diff = rate * 20 / 1000;

这里可以看到,115200 波特率一下时钟固定为24M,然后针对 230400/1152000 做了特殊处理,其他情况下,时钟频率就只有默认的16倍了,对于过采样而言,没有余量。

修改的话,比较简单,可以考虑将115200到1152000之间,统一做倍频处理:

1
2
3
4
5
6
		if (baud <= 115200)
			rate = 24000000;
		else if (baud <= 1152000)
			rate = baud * 16 * 2;
		else
			rate = baud * 16;

然后重新构建内核镜像并刷入,经过多次重启和断电测试,报错消失。

虽然可以通过优化电气连接等方法避免,但是某些情况也不是很方便做到,提高时钟频率算是一个 更为合适的做法。