Colibri iMX7D 1GB EMMC V1.1B
Custom Carrier Board
Linux BSP 6.4.0
Hello
I’ve got problem with second ethernet controller on Colibri-imx7d EMMC. As far as my investigations goes it seems that the reason is no clock signal on pin MX7D_PAD_EPDC_BDR0__CCM_ENET_REF_CLK2
We want to use the same PHY ic as on Colibri module: Micrel KSZ8041 with RMII mode, but with SFP module. We did this many times on our boards with iMX6ULL from different vendor than Toradex, and it worked without issues. This is first time with iMX7D.
The MDIO communication is working, PHY is detected by kernel but nothing happens when fiber/ethernet cable is connected(I’ve also soldered standard Ethernet socket to debug the issue).
[ 1.332645] fec 30be0000.ethernet eth0: registered PHC device 0
[ 1.981441] fec 30bf0000.ethernet eth1: registered PHC device 1
[ 8.052666] Micrel KSZ8041 30bf0000.ethernet-2:01: attached PHY driver (mii_bus:phy_addr=30bf0000.ethernet-2:01, irq=POLL)
[ 8.286216] Micrel KSZ8041 30be0000.ethernet-1:00: attached PHY driver (mii_bus:phy_addr=30be0000.ethernet-1:00, irq=POLL)
[ 10.392130] fec 30be0000.ethernet eth0: Link is Up - 100Mbps/Full - flow control rx/tx
Here is my device-tree config(based on this patch colibri_imx7_2nd_ethernet/0001-imx7d-2nd-ethernet-support_update_20200218.patch at master · simonqin09/colibri_imx7_2nd_ethernet · GitHub):
&fec2 {
pinctrl-names = "default", "sleep";
pinctrl-0 = <&pinctrl_enet2>;
pinctrl-1 = <&pinctrl_enet2_sleep>;
clocks = <&clks IMX7D_ENET2_IPG_ROOT_CLK>,
<&clks IMX7D_ENET_AXI_ROOT_CLK>,
<&clks IMX7D_ENET2_TIME_ROOT_CLK>,
<&clks IMX7D_PLL_ENET_MAIN_50M_CLK>,
<&clks IMX7D_ENET_PHY_REF_ROOT_DIV>;
clock-names = "ipg", "ahb", "ptp",
"enet_clk_ref", "enet_out";
assigned-clocks = <&clks IMX7D_ENET2_TIME_ROOT_SRC>,
<&clks IMX7D_ENET2_TIME_ROOT_CLK>,
<&clks IMX7D_ENET_PHY_REF_ROOT_DIV>;
assigned-clock-parents = <&clks IMX7D_PLL_ENET_MAIN_100M_CLK>;
assigned-clock-rates = <0>, <100000000>, <50000000>;
phy-mode = "rmii";
phy-handle = <ðphy1>;
phy-supply = <®_eth1_vbus>;
phy-reset-gpios = <&gpio2 30 GPIO_ACTIVE_LOW>;
phy-reset-duration = <500>;
phy-reset-post-delay = <100>;
fsl,magic-packet;
fsl,mii-exclusive;
nvmem-cells = <ð1_macaddr>;
nvmem-cell-names = "mac-address";
status = "okay";
mdio {
#address-cells = <1>;
#size-cells = <0>;
ethphy1: ethernet-phy@1 {
compatible = "ethernet-phy-ieee802.3-c22";
reg = <1>;
micrel,fiber-mode; /* remove this when testing standard ethernet */
};
};
};
pinctrl_enet2: enet2grp {
fsl,pins = <
MX7D_PAD_GPIO1_IO14__ENET2_MDIO 0x1b0b0 /* SODIMM 188 */
MX7D_PAD_GPIO1_IO15__ENET2_MDC 0x1b0b0 /* SODIMM 178 */
MX7D_PAD_EPDC_SDCE0__ENET2_RGMII_RX_CTL 0x73 /* SODIMM 122 */
MX7D_PAD_EPDC_SDCLK__ENET2_RGMII_RD0 0x73 /* SODIMM 114 */
MX7D_PAD_EPDC_SDLE__ENET2_RGMII_RD1 0x73 /* SODIMM 116 */
MX7D_PAD_EPDC_SDCE1__ENET2_RX_ER 0x73 /* SODIMM 124 */
MX7D_PAD_EPDC_GDRL__ENET2_RGMII_TX_CTL 0x73 /* SODIMM 133 */
MX7D_PAD_EPDC_SDCE2__ENET2_RGMII_TD0 0x73 /* SODIMM 127 */
MX7D_PAD_EPDC_SDCE3__ENET2_RGMII_TD1 0x73 /* SODIMM 130 */
MX7D_PAD_EPDC_BDR0__CCM_ENET_REF_CLK2 0x40000073 /* SODIMM 106 */
MX7D_PAD_EPDC_PWR_COM__GPIO2_IO30 0x73 /* SODIMM 112 - RST */
>;
};
pinctrl_enet2_sleep: enet2sleepgrp {
fsl,pins = <
MX7D_PAD_GPIO1_IO14__GPIO1_IO14 0x0
MX7D_PAD_GPIO1_IO15__GPIO1_IO15 0x0
MX7D_PAD_EPDC_SDCE0__GPIO2_IO20 0x0
MX7D_PAD_EPDC_SDCLK__GPIO2_IO16 0x0
MX7D_PAD_EPDC_SDLE__GPIO2_IO17 0x0
MX7D_PAD_EPDC_SDCE1__GPIO2_IO21 0x0
MX7D_PAD_EPDC_GDRL__GPIO2_IO26 0x0
MX7D_PAD_EPDC_SDCE2__GPIO2_IO22 0x0
MX7D_PAD_EPDC_SDCE3__GPIO2_IO23 0x0
MX7D_PAD_EPDC_BDR0__GPIO2_IO28 0x0
>;
};
We have found out that 50MHz clock signal form MX7D_PAD_EPDC_BDR0__CCM_ENET_REF_CLK2 is produced only for a short time on boot. And disappears when this boot message is shown:
[ 8.052666] Micrel KSZ8041 30bf0000.ethernet-2:01: attached PHY driver (mii_bus:phy_addr=30bf0000.ethernet-2:01, irq=POLL)
[ 8.286216] Micrel KSZ8041 30be0000.ethernet-1:00: attached PHY driver (mii_bus:phy_addr=30be0000.ethernet-1:00, irq=POLL)
As far as I know there is an external 50MHz oscillator used to drive fec1 controller on Colibri board. So generation of ref_clock from Colibri board is not needed.
I’ve found also this function in currently used kernel source:
https://git.toradex.com/cgit/linux-toradex.git/tree/arch/arm/mach-imx/mach-imx7d.c?h=toradex_5.15-2.2.x-imx
static void __init imx7d_enet_clk_sel(void)
{
struct regmap *gpr;
gpr = syscon_regmap_lookup_by_compatible("fsl,imx7d-iomuxc-gpr");
if (!IS_ERR(gpr)) {
regmap_update_bits(gpr, IOMUXC_GPR1, IMX7D_GPR1_ENET_TX_CLK_SEL_MASK, 0);
regmap_update_bits(gpr, IOMUXC_GPR1, IMX7D_GPR1_ENET_CLK_DIR_MASK, 0);
} else {
pr_err("failed to find fsl,imx7d-iomux-gpr regmap\n");
}
}
I’ve tried to create patches with that ones from previous versions of kernel:
https://git.toradex.com/cgit/linux-toradex.git/tree/arch/arm/mach-imx/mach-imx7d.c?h=toradex_4.1-2.0.x-imx
static void __init imx7d_enet_clk_sel(void)
{
struct device_node *np;
struct clk *enet_out_clk;
struct regmap *gpr;
np = of_find_compatible_node(NULL, NULL, "fsl,imx7d-fec");
if (!np) {
pr_warn("%s: failed to find fec node\n", __func__);
return;
}
enet_out_clk = of_clk_get_by_name(np, "enet_out");
gpr = syscon_regmap_lookup_by_compatible("fsl,imx7d-iomuxc-gpr");
if (!IS_ERR(gpr)) {
if (IS_ERR(enet_out_clk)) {
pr_info("%s: failed to get enet_out clock, assuming ext. clock source\n", __func__);
/* use external clock for PHY */
regmap_update_bits(gpr, IOMUXC_GPR1, IMX7D_GPR1_ENET_TX_CLK_SEL_MASK, IMX7D_GPR1_ENET_TX_CLK_SEL_MASK);
regmap_update_bits(gpr, IOMUXC_GPR1, IMX7D_GPR1_ENET_CLK_DIR_MASK, 0);
} else {
pr_info("%s: found enet_out clock, assuming internal clock source\n", __func__);
/* use internal clock generation and output it to PHY */
regmap_update_bits(gpr, IOMUXC_GPR1, IMX7D_GPR1_ENET_TX_CLK_SEL_MASK, 0);
regmap_update_bits(gpr, IOMUXC_GPR1, IMX7D_GPR1_ENET_CLK_DIR_MASK, IMX7D_GPR1_ENET1_CLK_DIR_MASK);
clk_put(enet_out_clk);
}
} else {
pr_err("failed to find fsl,imx7d-iomux-gpr regmap\n");
}
of_node_put(np);
}
static void __init imx7d_enet_clk_sel(void)
{
struct device_node *np = NULL;
struct clk *enet_out_clk;
struct regmap *gpr;
gpr = syscon_regmap_lookup_by_compatible("fsl,imx7d-iomuxc-gpr");
if (IS_ERR(gpr)) {
pr_err("failed to find fsl,imx7d-iomux-gpr regmap\n");
return;
}
do {
int id;
u32 clk_sel_mask, clk_dir_mask;
np = of_find_compatible_node(np, NULL, "fsl,imx7d-fec");
if (!np)
return;
/* Determine controller ID by ethernet alias */
id = of_alias_get_id(np, "ethernet");
clk_sel_mask = id == 0 ? IMX7D_GPR1_ENET1_TX_CLK_SEL_MASK :
IMX7D_GPR1_ENET2_TX_CLK_SEL_MASK;
clk_dir_mask = id == 0 ? IMX7D_GPR1_ENET1_CLK_DIR_MASK :
IMX7D_GPR1_ENET2_CLK_DIR_MASK;
enet_out_clk = of_clk_get_by_name(np, "enet_out");
if (IS_ERR(enet_out_clk)) {
pr_info("%s: fec%d: failed to get enet_out clock, assuming ext. clock source\n",
__func__, id + 1);
/* use external clock for PHY */
regmap_update_bits(gpr, IOMUXC_GPR1, clk_sel_mask, clk_sel_mask);
regmap_update_bits(gpr, IOMUXC_GPR1, clk_dir_mask, 0);
} else {
pr_info("%s: fec%d: found enet_out clock, assuming internal clock source\n",
__func__, id + 1);
/* use internal clock generation and output it to PHY */
regmap_update_bits(gpr, IOMUXC_GPR1, clk_sel_mask, 0);
regmap_update_bits(gpr, IOMUXC_GPR1, clk_dir_mask, clk_dir_mask);
clk_put(enet_out_clk);
}
} while (np);
}
But without luck. None of them worked. There only appeared this messages from kernel:
[ 0.066892] imx7d_enet_clk_sel: fec1: failed to get enet_out clock, assuming ext. clock source
[ 0.066943] imx7d_enet_clk_sel: fec2: found enet_out clock, assuming internal clock source
Still:
u-boot → 50MHz on MX7D_PAD_EPDC_BDR0__CCM_ENET_REF_CLK2 →
[ 8.052666] Micrel KSZ8041 30bf0000.ethernet-2:01: attached PHY driver (mii_bus:phy_addr=30bf0000.ethernet-2:01, irq=POLL)
→ clock off → fec2 not working
Here is the enet clocks summary:
cat /sys/kernel/debug/clk/clk_summary
enable prepare protect duty hardware
clock count count count rate accuracy phase cycle enable
-------------------------------------------------------------------------------------------------------
pll_enet_main 1 1 0 1000000000 0 0 50000 Y
pll_enet_main_bypass 1 1 0 1000000000 0 0 50000 Y
pll_enet_main_clk 2 2 0 1000000000 0 0 50000 Y
pll_enet_25m 0 0 0 25000000 0 0 50000 Y
pll_enet_25m_clk 0 0 0 25000000 0 0 50000 N
pll_enet_40m 0 0 0 40000000 0 0 50000 Y
pll_enet_40m_clk 0 0 0 40000000 0 0 50000 N
wrclk_src 0 0 0 40000000 0 0 50000 Y
wrclk_cg 0 0 0 40000000 0 0 50000 N
wrclk_pre_div 0 0 0 40000000 0 0 50000 Y
wrclk_post_div 0 0 0 40000000 0 0 50000 Y
wrclk_root_clk 0 0 0 40000000 0 0 50000 N
pll_enet_50m 1 1 0 50000000 0 0 50000 Y
pll_enet_50m_clk 2 2 0 50000000 0 0 50000 Y
enet2_ref_src 0 0 0 50000000 0 0 50000 Y
enet2_ref_cg 0 0 0 50000000 0 0 50000 N
enet2_ref_pre_div 0 0 0 50000000 0 0 50000 Y
enet2_ref_post_div 0 0 0 50000000 0 0 50000 Y
enet1_ref_src 0 0 0 50000000 0 0 50000 Y
enet1_ref_cg 0 0 0 50000000 0 0 50000 N
enet1_ref_pre_div 0 0 0 50000000 0 0 50000 Y
enet1_ref_post_div 0 0 0 50000000 0 0 50000 Y
pll_enet_100m 1 1 0 100000000 0 0 50000 Y
pll_enet_100m_clk 2 2 0 100000000 0 0 50000 Y
enet2_time_src 1 1 0 100000000 0 0 50000 Y
enet2_time_cg 1 1 0 100000000 0 0 50000 Y
enet2_time_pre_div 1 1 0 100000000 0 0 50000 Y
enet2_time_post_div 1 1 0 100000000 0 0 50000 Y
enet2_time_root_clk 1 1 0 100000000 0 0 50000 Y
enet1_time_src 1 1 0 100000000 0 0 50000 Y
enet1_time_cg 1 1 0 100000000 0 0 50000 Y
enet1_time_pre_div 1 1 0 100000000 0 0 50000 Y
enet1_time_post_div 1 1 0 100000000 0 0 50000 Y
enet1_time_root_clk 1 1 0 100000000 0 0 50000 Y
pll_enet_125m 0 0 0 125000000 0 0 50000 Y
pll_enet_125m_clk 0 0 0 125000000 0 0 50000 N
pll_enet_250m 0 0 0 250000000 0 0 50000 Y
pll_enet_250m_clk 0 0 0 250000000 0 0 50000 N
pll_enet_500m 0 0 0 500000000 0 0 50000 Y
pll_enet_500m_clk 0 0 0 500000000 0 0 50000 N
And some links connected with that issue: