作者:树哥
邮箱:[email protected]
一、前言
最近在ODYSSEY-STM32MP157C板子上移植arm-trusted-firmware、u-boot以及linux(其实这个开发板官方是有提供完整的系统镜像的,重新移植的原因主要是官方镜像没有使用TF-A,而是使用Uboot-SPL替代TF-A作为FSBL启动)。本以为之前在另一块stm32mp157板子上面移植过这些东西所以应该问题不大,但是由于硬件设计的差异仍然是遇到了一些问题。最后还是花费了些时间解决问题,故写这篇笔记用来记录各环节遇到的问题及解决过程。
二、Trusted Firmware A (TF-A)
1、问题
ST官方的开发板都是使用芯片的I2C4驱动PMIC,而这块开发板是用的I2C2接口,本以为将设备树中驱动PMIC节点的i2c4部分改为i2c2,然后添加上i2c2的pinctrl信息就可以了。然而并非如此,会报如下错误:
PANIC at PC : 0x2ffeeab3
Exception mode=0x00000016 at: 0x2ffeeab3
2、原因
I2C4或I2C6被定义为安全外设,而I2C2为非安全外设。
在源码drivers/st/pmic/stm32mp_pmic.c
中PMIC初始化函数调用如下:
void initialize_pmic(void)
--> initialize_pmic_i2c() //从设备树获取pmic的i2c节点信息并初始化
--> register_non_secure_pmic()
--> stm32mp_register_non_secure_periph_iomem(i2c_handle.i2c_base_addr)
--> register_periph_iomem(base, SHRES_NON_SECURE)
由于在函数register_periph_iomem
中没有定义I2C_BASE导致进入panic()
函数。
3、解决方法
在文件plat/st/stm32mp1/stm32mp1_def.h
中定义I2C2_BASE:
#define I2C2_BASE U(0x40013000)
然后修改文件plat/st/stm32mp1/stm32mp1_shared_resources.c
中的函数register_periph_iomem
如下:
case UART8_BASE:
case IWDG2_BASE:
case I2C2_BASE: //添加非安全外设I2C2
/* Allow drivers to register some non-secure resources */
VERBOSE("IO for non-secure resource 0x%x\n",
(unsigned int)base);
if (state != SHRES_NON_SECURE) {
panic();
}
return;
default:
panic();
break;
三、U-Boot
1、问题
移植完成第一次进入u-boot首先出现如下提示:
invalid MAC address in OTP 00:00:00:00:00:00
Net:
Error: ethernet@5800a000 address not set.
No ethernet found.
设置ethaddr
环境变量即可:
env set ethaddr 14:16:0B:0B:11:31
上面这个倒是不算问题,重点是接下来遇到的问题。在执行dhcp
命令时遇到如下错误:
STM32MP> dhcp
EQOS_DMA_MODE_SWR stuckFAILED: -110STM32MP>
这显然是网卡有问题。
2、原因
查看硬件原理图以太网部分发现和ST官方的DK2开发板有些不同。官方开发板的千兆以太网(RGMII)的125MHZ时钟来自PHY芯片,而我手上这块开发板的设计默认是需要由MPU内部RCC提供125MHZ时钟。除此之外,PHY地址硬件设计上也不相同,需要修改。
在STM32MP157参考手册(RM0436)中的Figure 83对以太网时钟有详细的描述
由上图可知若要使用RCC作为125MHZ时钟来源则需要配置SYSCFG寄存器的ETH_CLK_SEL字段,除此之外还需要选择使用RCC的pll4_p_ck或是pll3_q_ck作为具体时钟来源(我选择的是PLL4P)。既然知道了问题所在,接下来就是修改代码适配硬件。
3、解决方法
首先是时钟部分的改动。因为使用了TF-A所以U-Boot配置为trusted模式,修改u-boot源码中的时钟配置是无效的,需要修改TF-A中的时钟配置。主要就是配置PLL4P输出125MHZ时钟,关于PLL的详细信息需要查阅参考手册(RM0436)的第10章RCC(Reset and clock control)的PLL章节。
在TF-A源码设备树的rcc节点中添加如下代码配置PLL4时钟频率:
&rcc {
......
/* VCO = 750.0 MHz => P = 125, Q = 62.5, R = 62.5 */
pll4: st,pll@3 {
compatible = "st,stm32mp1-pll";
reg = <3>;
cfg = < 3 124 5 11 11 PQR(1,1,1) >;
};
......
};
cfg中参数含义如下:
cfg = <DIVM DIVN DIVP DIVQ DIVR Output(DIVPEN DIVQEN DIVREN)>
因此按照上述参数计算各部分时钟频率结果如下:
F(ref_ck) = HSE / (DIVM + 1)
Using the PLLs in integer mode
For the PLL3 and PLL4, the VCO frequency (FVCO) is given by the following expression:
F(VCO) = F(ref_ck) x (DIVN + 1)
And the frequency at the output of the post-dividers is given by with y = P, Q or R:
F(pll_y_ck) = F(VCO) / (DIVy + 1)
VCO = HSE / (DIVM + 1) * (DIVN + 1) = 24MHZ / (3 + 1) * (124 + 1) = 750MHZ
PLL4P = P = VCO / (DIVP + 1) = 750MHZ / (5 + 1) = 125MHZ
PLL4Q = VCO / (DIVQ + 1) = 750MHZ / (11 + 1) = 62.5MHZ
PLL4R = VCO / (DIVR + 1) = 750MHZ / (11 + 1) = 62.5MHZ
这样就得到了期望的125MHZ时钟。接着还需要将Ethernet的时钟选为PLL4P,在rcc节点修改如下内容:
&rcc {
......
st,pkcs = <
......
- CLK_ETH_DISABLED //删除这个
+ CLK_ETH_PLL4P //添加这个
......
>;
......
};
至此,千兆以太网125MHZ时钟频率就配置好了。接下来需要修改的就是u-boot源码的设备树中ethernet0
节点内容,主要是修改时钟选择部分的内容。根据手册可知选择RCC作为千兆以太网125 MHz时钟需要将寄存器SYSCFG_PMCSETR的ETH_CLK_SEL位配置为1:
在源码drivers/net/dwc_eth_qos.c
文件中eqos_probe_resources_stm32
函数会调用board_interface_eth_init
函数(文件路径board/st/stm32mp1/stm32mp1.c
)根据设备树内容配置该寄存器该位,如下:
/* eth init function : weak called in eqos driver */
int board_interface_eth_init(struct udevice *dev,
phy_interface_t interface_type)
{
u8 *syscfg;
u32 value;
bool eth_clk_sel_reg = false;
......
/* Gigabit Ethernet 125MHz clock selection. */
eth_clk_sel_reg = dev_read_bool(dev, "st,eth_clk_sel");
......
syscfg = (u8 *)syscon_get_first_range(STM32MP_SYSCON_SYSCFG);
......
switch (interface_type) {
......
case PHY_INTERFACE_MODE_RGMII:
case PHY_INTERFACE_MODE_RGMII_ID:
case PHY_INTERFACE_MODE_RGMII_RXID:
case PHY_INTERFACE_MODE_RGMII_TXID:
if (eth_clk_sel_reg)
value = SYSCFG_PMCSETR_ETH_SEL_RGMII |
SYSCFG_PMCSETR_ETH_CLK_SEL;
else
value = SYSCFG_PMCSETR_ETH_SEL_RGMII;
debug("%s: PHY_INTERFACE_MODE_RGMII\n", __func__);
break;
......
}
/* clear and set ETH configuration bits */
writel(SYSCFG_PMCSETR_ETH_SEL_MASK | SYSCFG_PMCSETR_ETH_SELMII |
SYSCFG_PMCSETR_ETH_REF_CLK_SEL | SYSCFG_PMCSETR_ETH_CLK_SEL,
syscfg + SYSCFG_PMCCLRR);
writel(value, syscfg + SYSCFG_PMCSETR);
return 0;
}
因此需要在设备树ethernet0
节点下添加st,eth_clk_sel
(文件路径arch/arm/dts/stm32mp151.dtsi
)如下:
ethernet0: ethernet@5800a000 {
......
st,eth_clk_sel;
......
};
但是只是这么做还不够,进入u-boot会提示如下:
Net: No phy clock provided -61eth0: ethernet@5800a000
根据提示可以定位到代码位置在drivers/net/dwc_eth_qos.c
的eqos_probe_resources_stm32
函数中:
/* Get ETH_CLK clocks (optional) */
ret = clk_get_by_name(dev, "eth-ck", &eqos->clk_ck);
if (ret)
pr_warn("No phy clock provided %d", ret);
因此在设备树ethernet0添加eth-ck
如下:
ethernet0: ethernet@5800a000 {
......
clock-names = "stmmaceth",
"mac-clk-tx",
"mac-clk-rx",
+ "eth-ck", //添加这行
"ethstp";
......
st,eth_clk_sel;
......
};
但是但是,这么做依旧不够,修改后u-boot执行dhcp命令报错如下:
STM32MP> dhcp
stm32mp1_clk_get_id: clk id 112 not found
clk_enable(clk_ck) failed: -22eqos_start_clks() failed: -22FAILED: -22STM32MP>
因为添加了时钟名后还需要添加对应的时钟节点,因此在设备树ethernet0节点添加ETHCK_K
如下:
ethernet0: ethernet@5800a000 {
......
clock-names = "stmmaceth",
"mac-clk-tx",
"mac-clk-rx",
"eth-ck",
"ethstp";
clocks = <&rcc ETHMAC>,
<&rcc ETHTX>,
<&rcc ETHRX>,
+ <&rcc ETHCK_K>, //添加这行
<&rcc ETHSTP>;
st,eth_clk_sel;
......
};
最后还得修改phy地址以及添加phy复位引脚(需和硬件设计一致),否则会有如下报错提示:
地址错误:
STM32MP> dhcp
Could not get PHY for ethernet@5800a000: addr 0
phy_connect() failedFAILED: 0
复位引脚缺失错误
Net: gpio_request_by_name(phy reset) not provided -2eth0: ethernet@5800a000
修改如下:
ðernet0 {
......
mdio0 {
......
phy0: ethernet-phy@7 {
reg = <7>; //phy地址
reset-gpios = <&gpiog 0 GPIO_ACTIVE_LOW>; //phy复位引脚
};
};
};
复位操作在源码drivers/net/dwc_eth_qos.c
函数eqos_probe_resources_stm32
匹配设备树,函数eqos_start_resets_stm32
执行。
以上修改完成后重新编译u-boot启动后执行网络操作命令就正常了:
STM32MP> dhcp
ethernet@5800a000 Waiting for PHY auto negotiation to complete........... done
BOOTP broadcast 1
BOOTP broadcast 2
BOOTP broadcast 3
BOOTP broadcast 4
DHCP client bound to address 192.168.10.85 (2875 ms)
STM32MP>
四、Linux Kernel
1、问题
既然U-Boot部分网络有问题,那么在Linux中也会有同样的问题。但是在linux中只做u-boot中的改动网络仍然无法正常工作,会报如下错误:
root@arm:~# [ 316.042400] stm32-dwmac 5800a000.ethernet eth0: no phy at addr -1
[ 316.047061] stm32-dwmac 5800a000.ethernet eth0: stmmac_open: Cannot attach to PHY (error: -19)
[ 316.124044] stm32-dwmac 5800a000.ethernet eth0: no phy at addr -1
[ 316.128703] stm32-dwmac 5800a000.ethernet eth0: stmmac_open: Cannot attach to PHY (error: -19)
[ 316.186944] stm32-dwmac 5800a000.ethernet eth0: no phy at addr -1
[ 316.191693] stm32-dwmac 5800a000.ethernet eth0: stmmac_open: Cannot attach to PHY (error: -19)
[ 316.242397] stm32-dwmac 5800a000.ethernet eth0: no phy at addr -1
[ 316.247066] stm32-dwmac 5800a000.ethernet eth0: stmmac_open: Cannot attach to PHY (error: -19)
2、原因
在Linux设备树的ethernet0节点还需要添加一些phy芯片信息(这个板子是KSZ9031)并且要配置相关的驱动。
3、解决方法
make menuconfig
Linux/arm 5.4.192 Kernel Configuration
Device Drivers --->
[*] Network device support --->
-*- PHY Device support and infrastructure --->
<*> Micrel PHYs
完整的ethernet0设备树内容如下:
ethernet0: ethernet@5800a000 {
compatible = "st,stm32mp1-dwmac", "snps,dwmac-4.20a";
reg = <0x5800a000 0x2000>;
reg-names = "stmmaceth";
interrupts-extended = <&intc GIC_SPI 61 IRQ_TYPE_LEVEL_HIGH>,
<&exti 70 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "macirq",
"eth_wake_irq";
clock-names = "stmmaceth",
"mac-clk-tx",
"mac-clk-rx",
+ "eth-ck", //添加这行
"ethstp";
clocks = <&rcc ETHMAC>,
<&rcc ETHTX>,
<&rcc ETHRX>,
+ <&rcc ETHCK_K>, //添加这行
<&rcc ETHSTP>;
+ st,eth-clk-sel; //添加这行
st,syscon = <&syscfg 0x4>;
snps,mixed-burst;
snps,pbl = <2>;
snps,en-tx-lpi-clockgating;
snps,axi-config = <&stmmac_axi_config_0>;
snps,tso;
power-domains = <&pd_core>;
status = "disabled";
stmmac_axi_config_0: stmmac-axi-config {
snps,wr_osr_lmt = <0x7>;
snps,rd_osr_lmt = <0x7>;
snps,blen = <0 0 0 0 16 8 4>;
};
};
ðernet0 {
status = "okay";
pinctrl-0 = <ðernet0_rgmii_pins_a>;
pinctrl-1 = <ðernet0_rgmii_pins_sleep_a>;
pinctrl-names = "default", "sleep";
phy-mode = "rgmii";
max-speed = <1000>;
phy-handle = <&phy0>;
mdio0 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "snps,dwmac-mdio";
phy0: ethernet-phy@7 {
/* PHY_ID_KSZ9031 0x00221620 */
+ compatible = "ethernet-phy-id0022.1620"; //添加这行
reset-gpios = <&gpiog 0 GPIO_ACTIVE_LOW>; //phy复位引脚
reset-assert-us = <1000>;
reset-deassert-us = <2000>;
reg = <7>; //phy地址
};
};
};
重新编译linux镜像和设备树,启动后有如下提示信息说明网络正常了
[ 15.235404] stm32-dwmac 5800a000.ethernet eth0: PHY [stmmac-0:07] driver [Micrel KSZ9031 Gigabit PHY]
[ 15.275230] dwmac4: Master AXI performs any burst length
[ 15.281043] stm32-dwmac 5800a000.ethernet eth0: No Safety Features support found
[ 15.287068] stm32-dwmac 5800a000.ethernet eth0: IEEE 1588-2008 Advanced Timestamp supported
[ 15.287593] stm32-dwmac 5800a000.ethernet eth0: registered PTP clock
[ 15.287621] stm32-dwmac 5800a000.ethernet eth0: configuring for phy/rgmii link mode
[ 18.391932] stm32-dwmac 5800a000.ethernet eth0: Link is Up - 1Gbps/Full - flow control off
五、Root File System
1、问题
会自动报如下错误:
EXT4-fs (mmcblk0p4): error count since last fsck: 1
EXT4-fs (mmcblk0p4): initial error at time 1667385179: ext4_get_journal_inode:5149: inode 8
执行apt update
命令时报如下错误:
E: List directory /var/lib/apt/lists/partial is missing. - Acquire (30: Read-only file system)
2、原因
文件系统损坏
3、解决方法
重新制作文件系统烧录SD卡启动后问题解决
至此,在ODYSSEY-STM32MP157C开发板系统移植过程中遇到的这些困扰时长两年半的问题就都解决了