总结一次为R329开启uart的经历

sipeed出的maix sense开发板虽然宣称有5个串口,但是uart0用作了debug,uart1用作的Bluetooth,uart2没引出来,uart3只引出了RX和CTS,需要完整使用串口的话,只能使用ruart,并且maixsense上面预留的4Pin uart也接的也是这个。
image
好巧不巧的是,sipeed发布的第一版armbian镜像并没有启用其他串口,对ttyS2-ttyS5操作没有任何用,只能手动编辑设备树。

查看设备树

maix sense的kernel官方已经上传到github上,先clone下来
git clone -b r329-wip --depth 1 https://github.com/sipeed/linux.git
设备树在/linux/arch/arm64/boot/dts/allwinner/路径下,和r329相关的设备树有

sun50i-r329-maix-iia.dtsi
sun50i-r329-maixsense.dts
sun50i-r329.dtsi

三个。
其中uart的描述在中sun50i-r329.dtsi,如下:

uart0: serial@2500000 {
	compatible = "snps,dw-apb-uart";
	reg = <0x02500000 0x400>;
	interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
	reg-shift = <2>;
	reg-io-width = <4>;
	clocks = <&ccu CLK_BUS_UART0>;
	resets = <&ccu RST_BUS_UART0>;
	status = "disabled";
};

uart1: serial@2500400 {
	compatible = "snps,dw-apb-uart";
	reg = <0x02500400 0x400>;
	interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>;
	reg-shift = <2>;
	reg-io-width = <4>;
	clocks = <&ccu CLK_BUS_UART1>;
	resets = <&ccu RST_BUS_UART1>;
	status = "disabled";
};

uart2: serial@2500800 {
	compatible = "snps,dw-apb-uart";
	reg = <0x02500800 0x400>;
	interrupts = <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>;
	reg-shift = <2>;
	reg-io-width = <4>;
	clocks = <&ccu CLK_BUS_UART2>;
	resets = <&ccu RST_BUS_UART2>;
	status = "disabled";
};

uart3: serial@2500c00 {
	compatible = "snps,dw-apb-uart";
	reg = <0x02500c00 0x400>;
	interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>;
	reg-shift = <2>;
	reg-io-width = <4>;
	clocks = <&ccu CLK_BUS_UART3>;
	resets = <&ccu RST_BUS_UART3>;
	status = "disabled";
};

引脚定义也在该文件中

uart0_pb_pins: uart0-pb-pins {
	pins = "PB4", "PB5";
	function = "uart0";
};

uart1_pg_pins: uart1-pg-pins {
	pins = "PG6", "PG7";
	function = "uart1";
};

uart fuction在另外两个文件中,其中uart0被用做了stdout-path,uart1被用作了bluetooth

aliases {
	serial0 = &uart0;
	mmc0 = &mmc0;
	};

chosen {
	stdout-path = "serial0:115200n8";
};

&uart0 {
	pinctrl-names = "default";
	pinctrl-0 = <&uart0_pb_pins>;
	status = "okay";
};

&uart1 {
	pinctrl-names = "default";
	pinctrl-0 = <&uart1_pg_pins>, <&uart1_pg_rts_cts_pins>;
	uart-has-rtscts;
	status = "okay";

	bluetooth {
		compatible = "realtek,rtl8723ds-bt";
		device-wake-gpios = <&r_pio 1 1 GPIO_ACTIVE_HIGH>; /* PM1 */
		host-wake-gpios = <&r_pio 1 3 GPIO_ACTIVE_HIGH>; /* PM3 */
		enable-gpios = <&r_pio 1 2 GPIO_ACTIVE_HIGH>; /* PM2 */
		max-speed = <1500000>;
	};
};

添加uart3节点

可以看到,只有uart0和uart1定义了pinctrl-0和function,
其他uart没有定义,所以在linux中没法使用。
uart3已经描述,要开启uart3,只需要添加uart3的pinctrl-0和function即可。
照葫芦画瓢,在sun50i-r329.dtsi 文件中添加pinctrl-0

uart3_ph_pins: uart3-ph-pins {
	pins = "PH4", "PH5";
	function = "uart3";
};

sun50i-r329-maixsense.dts中添加uart3 function

&uart3 {
	pinctrl-names = "default";
	pinctrl-0 = <&uart3_ph_pins>;
	status = "okay";
};

即可。
编译dtb make dtbs,放入maixsense的boot/dtb/allwinner/文件夹中,即可使用。

测试uart3

上电,PH5引脚接RXD。
查看ttyS*

maixsense:~:# ls /dev/ttyS*
/dev/ttyS0  /dev/ttyS2  /dev/ttyS3  /dev/ttyS4  /dev/ttyS5

没有出现ttyS1,可能为某个设备保留,测试ttyS2
使用minicom接收数据 minicom -D /dev/ttyS2

Welcome to minicom 2.8

OPTIONS: I18n 
Port /dev/ttyS2, 16:00:56

Press CTRL-A Z for help on special keys

hello sipeed

UART RXD正常。
但是发现lcd屏幕无法刷新了,查看原理图发现PH4连接了LCD的DC,导致了功能冲突。
修改sun50i-r329.dtsi,设置uart3_tx_pin为缺省值。

uart3_ph_pins: uart3-ph-pins {
	pins = "", "PH5";
	function = "uart3";
};

重新编译,上电,一切正常。

添加s_uart

uart3虽然能用了,但是只能收不能发,若要使用全功能uart,还需要启用s_uart。
设备树中并没有s_uart相关的描述,因此需要自行添加。
参考uart0的描述,其中reg,interrupts,clocks和resets我们都不知道,因此添加s_uart的重点就在于查找这些参数。

uart0: serial@2500000 {
	compatible = "snps,dw-apb-uart";
	reg = <0x02500000 0x400>;
	interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
	reg-shift = <2>;
	reg-io-width = <4>;
	clocks = <&ccu CLK_BUS_UART0>;
	resets = <&ccu RST_BUS_UART0>;
	status = "disabled";
};

查找技术手册

翻开R329 User Manual,找到7.2 UART一章
在7.2.5 Register List小节中,提供了uart的reg基址。

Module Name Base Address
UART0 0x02500000
UART1 0x02500400
UART2 0x02500800
UART3 0x02500C00
R_UART0 0x07080000
其中r_uart0就是我们需要的s_uart(为啥是?因为没别的了。为啥两个代号?我也不清楚)

reg get!

r_uart: serial@7080000 {
	compatible = "snps,dw-apb-uart";
	reg = <0x0708000 0x400>;
	reg-shift = <2>;
	reg-io-width = <4>;

接下来需要查找的参数是interrupts,找到3.8 Generic Interrupt Controller (GIC) 一章。
在Table 3-10 Interrupt Sources 表格中,描述了所有的中断号。

Module Name Interrupt Number
34 UART0
35 UART1
36 UART2
37 UART3
143 R_UART0
参考标准串口使用的GIC_SPI(SPI:shared processor interrupts 中断号 32 ~32+224),
得到r_uart的中断号143-32=111

Interrupt get!

r_uart: serial@7080000 {
	compatible = "snps,dw-apb-uart";
	reg = <0x0708000 0x400>;
	reg-shift = <2>;
	reg-io-width = <4>;

接下来就是clocks和resets。
参考设备树中其他信息,CPUS Domain Related的资源,也就是中断号120以后的资源,使用的是r_ccu

lradc: lradc@7030800 {
	compatible = "allwinner,sun50i-r329-lradc";
	reg = <0x07030800 0x400>;
	interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH>;
	clocks = <&r_ccu CLK_R_BUS_LRADC>;
	resets = <&r_ccu RST_R_BUS_LRADC>;
	status = "disabled";
};

其定义在

#include <dt-bindings/clock/sun50i-r329-r-ccu.h>
#include <dt-bindings/reset/sun50i-r329-r-ccu.h>

中。

All get!

r_uart: serial@7080000 {
	compatible = "snps,dw-apb-uart";
	reg = <0x07080000 0x400>;
	interrupts = <GIC_SPI 111 IRQ_TYPE_LEVEL_HIGH>;
	reg-shift = <2>;
	reg-io-width = <4>;
	clocks = <&r_ccu CLK_R_BUS_UART>;
	resets = <&r_ccu RST_R_BUS_UART>;
};

将s_uart的描述插入sun50i-r329.dtsi中。

配置s_uart

s_uart由cpus控制,因此要把相关描述放入r_pio下

	r_pio: pinctrl@7022000 {
		compatible = "allwinner,sun50i-r329-r-pinctrl";
		reg = <0x07022000 0x400>;
		interrupts = <GIC_SPI 107 IRQ_TYPE_LEVEL_HIGH>,
			     <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>,
			     <GIC_SPI 109 IRQ_TYPE_LEVEL_HIGH>;
		clocks = <&r_ccu CLK_R_APB1>, <&osc24M>, <&rtc 0>;
		clock-names = "apb", "hosc", "losc";
		gpio-controller;
		#gpio-cells = <3>;
		interrupt-controller;
		#interrupt-cells = <3>;

		r_uart_pins: r-uart-pins {
			pins = "PL8", "PL9";
			function = "s_uart";
		};

然后在sun50i-r329-maixsense.dts中开启r_uart

&r_uart{
	status = "okay";
};

r_uart的设备树配置就完成了。
但是实际上,在此时的版本中,r_uart还是无法使用。因为在pinctrl中,并没有cpus和cpux下的控制器定义,在Icenowy提交了该次pr后,才能正常使用。
新增的相关引脚配置如下

SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 7),
	  SUNXI_FUNCTION(0x0, "gpio_in"),
	  SUNXI_FUNCTION(0x1, "gpio_out"),
	  SUNXI_FUNCTION(0x2, "s_ir"),		/* RX */
	  SUNXI_FUNCTION(0x4, "clock"),		/* X32KFOUT */
	  SUNXI_FUNCTION(0x5, "s_pwm"),		/* PWM5 */
	  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 7)),	/* PL_EINT7 */
SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 8),
	  SUNXI_FUNCTION(0x0, "gpio_in"),
	  SUNXI_FUNCTION(0x1, "gpio_out"),
	  SUNXI_FUNCTION(0x2, "s_uart"),	/* TX */
	  SUNXI_FUNCTION(0x3, "s_i2c"),		/* SDA */
	  SUNXI_FUNCTION(0x4, "s_ir"),		/* RX */
	  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 8)),	/* PL_EINT8 */

可以发现实际上function = "s_uart";调用的function由SUNXI_FUNCTION(0x2, "s_uart")定义。

测试s_uart

上电测试时,发现了一个新问题,启动uboot时,log打印在uart0上,但是启动kernel以后,数据飞了。开始以为是内核卡死了,但是lcd有输出,于是使用ssh连接上,使用minicom操作串口后发现,ttyS0被连接到r_uart上了,uart0此时变成了ttyS2。
最后的解决办法是在aliases中给r_uart设置别名

	aliases {
		serial0 = &uart0;
		serial1 = &r_uart;
		mmc0 = &mmc0;
	};

经过测试,/dev/ttyS1只有经过aliases分配编号后才能使用,并且只有r_uart可以使用,并且r_uart只会使用/dev/ttyS0和/dev/ttyS1。
当未给r_uart分配编号时,r_uart会抢占/dev/ttyS0,因此所有信息由r_uart输出。
此时uartx会被分配到/dev/ttyS2以后。并且aliases只能分配serial0-serial3之间的编号,即/dev/ttyS0到/dev/ttyS3之间。在给uart分配serial4及以后的编号时,ttyS*不会有任何反应。(目前原因未知)
重新编译后,串口就能正常使用了。

maixsense:~:# ls /dev/ttyS*
/dev/ttyS0  /dev/ttyS1  /dev/ttyS3  /dev/ttyS4  /dev/ttyS5

|/dev/ttyS0| /dev/ttyS1 |/dev/ttyS3 |
| ---- | ---- |---- |---- |
| uart0|r_uart |uart3 |

总结

posted @ 2021-08-26 18:31  USTHzhanglu  阅读(1042)  评论(10编辑  收藏  举报