学弟和我说逐飞核心板的电机输出PWM引脚坏了,买新的要等到20号才能发货,问问我有没有办法从软件上修复。我想了一下,可以通过修改设备树的配置来达到相同的效果。
解读设备树
在逐飞开源库下载 LS2K0300 有关的开源资料,在虚拟机中输入以下命令。
git clone https://gitee.com/seekfree/LS2K0300_Library.git
打开其中的 linux 内核文件夹,打开目录 arch/loongarch/boot/dts/loongson,可以看到有许多设备树文件:
图 1 设备树文件
打开 seekfree_2k0300_coreboard.dts 文件,找到如下代码:
1 2 3 4 5 6 7 8
| zf_pwm_motor_2{ compatible = "seekfree,pwm"; pwms = <&pwm1 0 1000000>; status = "okay"; freq = <17000>; duty = <0>; duty_max = <10000>; };
|
可以看出,电机2需要一个 PWM1 控制器的 通道0 输出 pwm。接下来,我们在 loongson_2k0300.dtsi 找到 pwm1 控制器。
1 2 3 4 5 6 7 8 9
| pwm1: pwm@1611b010 { compatible = "loongson,ls300-pwm"; reg = <0 0x1611b010 0 0x10>; clock-frequency = <160000000>; interrupt-parent = <&icu>; interrupts = <16>; #pwm-cells = <2>; status = "disabled"; };
|
可以看出,他的寄存器在 0x1611b010,CPU 写这个地址就能控制 PWM 的输出。但此时信号还没有出去,因为还没有接到引脚上。于是,在 seekfree_2k0300_coreboard.dts 板级文件上,我们找到了 pwm1 控制器的出口:
1 2 3 4 5 6 7
| &pwm1{ status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&pwm1_mux_m1>; clock-frequency = <160000000>; compatible = "loongson,ls300-pwm"; };
|
所以,当 pwm1 控制器启用时,默认使用 pwm1_mux_m1 引脚输出 PWM 信号。那 pwm1_mux_m1 又是什么?我们在 2k0300-pinctrl.dtsi 文件中可以找到如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| pwm1_pin: pwm1-pin{ pwm1_mux_m0: pwm1-mux-m0{ loongson,pinmux = <&gpa4 1 1>; loongson,pinmux-funcsel = <PINCTL_FUNCTION1>; }; pwm1_mux_m1: pwm1-mux-m1{ /* pwm1 have three multiplex methods */ loongson,pinmux = <&gpa5 7 7>; loongson,pinmux-funcsel = <PINCTL_FUNCTION2>; }; pwm1_mux_m2: pwm1-mux-m2{ /* pwm1 have three multiplex methods */ loongson,pinmux = <&gpa6 7 7>; loongson,pinmux-funcsel = <PINCTL_FUNCTION2>; }; };
|
可以看出,pwm1_mux_m1 对应的引脚就是 <&gpa5 7 7>、第二功能复用,这个组又是什么意思呢?找到 gpa 有关的设备树代码:
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
| pinctrl: pinctrl@0x16000490 { compatible = "loongson,ls2k300-pinctrl"; reg = <0 0x16000490 0 0x10 0 0x16104000 0 0x1000>; loongson,regs-offset = <0x4>; loongson,num-chips = <7>; #address-cells = <2>; #size-cells = <2>;
gpa0: gpa0@0x16104000 { gpio-controller; #gpio-cells = <2>; #loongson,pinmux-cells = <2>;
interrupt-controller; #interrupt-cells = <3>; interrupt-parent = <&icu>; interrupts = <53>; };
gpa1: gpa1@0x16104000 { gpio-controller; #gpio-cells = <2>; #loongson,pinmux-cells = <2>;
interrupt-controller; #interrupt-cells = <3>; interrupt-parent = <&icu>; interrupts = <54>; };
gpa2: gpa2@0x16104000 { gpio-controller; #gpio-cells = <2>; #loongson,pinmux-cells = <2>;
interrupt-controller; #interrupt-cells = <3>; interrupt-parent = <&icu>; interrupts = <55>; };
gpa3: gpa3@0x16104000 { gpio-controller; #gpio-cells = <2>; #loongson,pinmux-cells = <2>;
interrupt-controller; #interrupt-cells = <3>; interrupt-parent = <&icu>; interrupts = <56>; };
gpa4: gpa4@0x16104000 { gpio-controller; #gpio-cells = <2>; #loongson,pinmux-cells = <2>;
interrupt-controller; #interrupt-cells = <3>; interrupt-parent = <&icu>; interrupts = <57>; };
gpa5: gpa5@0x16104000 { gpio-controller; #gpio-cells = <2>; #loongson,pinmux-cells = <2>;
interrupt-controller; #interrupt-cells = <3>; interrupt-parent = <&icu>; interrupts = <58>; };
gpa6: gpa6@0x16104000 { gpio-controller; #gpio-cells = <2>; #loongson,pinmux-cells = <2>;
interrupt-controller; #interrupt-cells = <3>; interrupt-parent = <&icu>; interrupts = <59>; }; };
|
可以看到,设备树将 GPIO 分为 7 个 bank,而查阅龙芯数据手册后得知,这块芯片一共有 106 个 GPIO,106 / 7 约等于 15,所以可以得知一组有 16 个 GPIO,因此:
1 2 3 4 5 6 7
| gpa0 : GPIO0 ~ GPIO15 gpa1 : GPIO16 ~ GPIO31 gpa2 : GPIO32 ~ GPIO47 gpa3 : GPIO48 ~ GPIO63 gpa4 : GPIO64 ~ GPIO79 gpa5 : GPIO80 ~ GPIO95 gpa6 : GPIO96 ~ GPIO111
|
验算一下,<&gpa5 7 7>,5*16 + 7 = 87,正好对应板子上的 P87,组中的第三位数据就表示写当前 GPIO 复用配置寄存器的第 [2x+1:2x]位。
图 2 复用寄存器
loongson,pinmux-funcsel = <PINCTL_FUNCTION2>;
这一句就表示使用当前 GPIO 的第二功能复用,查询数据手册,发现其第二功能正好是 pwm 输出。
图 3 GPIO 复用功能
总结一下,PWM 控制器只负责产生波形(channel0),而 pinctrl 把这个波形接到了 GPIO87。
修改设备树
很好,现在已经读懂了。根据学弟要求我们需要修改 PWM 输出到 P65 引脚,正好 pwm1_mux_m0 对应的就是 P65,因此,在 seekfree_2k0300_coreboard.dts 中,修改 pwm1_mux_m1 为 pwm1_mux_m0,就将输出引脚从<&gpa5 7 7> 改为了 <&gpa4 1 1>,即 P65。
1 2 3 4 5 6 7
| &pwm1{ status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&pwm1_mux_m0>; clock-frequency = <160000000>; compatible = "loongson,ls300-pwm"; };
|
接下来一步非常重要,检查此 GPIO 口有没有被其他设备占用。可以看到&gpa4 1 已被占用,我们把这个设备改成 disbaled。
1 2 3 4 5 6
| zf_gpio_hall_detection{ status = "disbaled"; compatible = "seekfree,gpio_in"; gpios = <&gpa4 1 GPIO_ACTIVE_HIGH>; };
|
学弟进行设备树文件的替换后,运行根目录下的 build.sh 脚本,PWM成功复活。