KSZ9031 is a very common PHY used with many ethernet design. This document will show you how to add it in u-boot and kernel.
1. Schematic
The MODE[3:0] strap-in pins are sampled and latched at power-up/reset. MODE[3:0]=1111 is RGMII mode - Advertise all capabilities (10/100/1000 speed half-/full-duplex)
The PHY address, PHYAD[2:0], is sampled and latched at power-up/reset. Here PHY address is set to 001.
In this design example, the ENET_RESET_B is connected to GPIO pin GPIO1_IO03.
2. Source code modification
In u-boot source code, add the following code in the <board_name>.c file.
- IOMUX setup for the GPIO1_IO03 pin.
static iomux_v3_cfg_t const phy_reset_pads[] = {
MX7D_PAD_GPIO1_IO03__GPIO1_IO3 | MUX_PAD_CTRL(NO_PAD_CTRL),
};
- In the function setup_fec(int fec_id), add the code for phy reset.
imx_iomux_v3_setup_multiple_pads(phy_reset_pads, ARRAY_SIZE(phy_reset_pads));
gpio_request(IMX_GPIO_NR(1, 3), "ENET PHY Reset");
gpio_direction_output(IMX_GPIO_NR(1, 3) , 0);
mdelay(20);
gpio_set_value(IMX_GPIO_NR(1, 3), 1);
- There is a PHY config for the KSZ9031.
int board_phy_config(struct phy_device *phydev)
{ /*
* Default setting for GMII Clock Pad Skew Register 0x1EF:
* MMD Address 0x2h, Register 0x8h
*
* GTX_CLK Pad Skew 0xF -> 0.9 nsec skew
* RX_CLK Pad Skew 0xF -> 0.9 nsec skew
*
* Adjustment -> write 0x3FF:
* GTX_CLK Pad Skew 0x1F -> 1.8 nsec skew
* RX_CLK Pad Skew 0x1F -> 1.8 nsec skew
*
*/
/* control data pad skew - devaddr = 0x02, register = 0x04 */
ksz9031_phy_extended_write(phydev, 0x02,
MII_KSZ9031_EXT_RGMII_CTRL_SIG_SKEW,
MII_KSZ9031_MOD_DATA_NO_POST_INC, 0x0000);
/* rx data pad skew - devaddr = 0x02, register = 0x05 */
ksz9031_phy_extended_write(phydev, 0x02,
MII_KSZ9031_EXT_RGMII_RX_DATA_SKEW,
MII_KSZ9031_MOD_DATA_NO_POST_INC, 0x0000);
/* tx data pad skew - devaddr = 0x02, register = 0x05 */
ksz9031_phy_extended_write(phydev, 0x02,
MII_KSZ9031_EXT_RGMII_TX_DATA_SKEW,
MII_KSZ9031_MOD_DATA_NO_POST_INC, 0x0000);
/* gtx and rx clock pad skew - devaddr = 0x02, register = 0x08 */
ksz9031_phy_extended_write(phydev, 0x02,
MII_KSZ9031_EXT_RGMII_CLOCK_SKEW,
MII_KSZ9031_MOD_DATA_NO_POST_INC, 0x03FF);
if (phydev->drv->config)
phydev->drv->config(phydev);
return 0;
}
The KSZ9031 driver (drivers/net/phy/micrel.c) had already supported in the u-boot source code. Add the following #define in the <board_name>.h file to enable the driver for building.
#define CONFIG_PHY_MICREL
As the PHY address on the board is 001, change the PHYADDR to 1 in the <board_name>.h file.
#define CONFIG_FEC_MXC_PHYADDR 0x1
In the kernel source code, add/modify the PHY setting in dts file like this.
&fec1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_enet1 &pinctrl_enet_reset>;
assigned-clocks = <&clks IMX7D_ENET_PHY_REF_ROOT_SRC>,
<&clks IMX7D_ENET_AXI_ROOT_SRC>,
<&clks IMX7D_ENET1_TIME_ROOT_SRC>,
<&clks IMX7D_ENET1_TIME_ROOT_CLK>,
<&clks IMX7D_ENET_AXI_ROOT_CLK>;
assigned-clock-parents = <&clks IMX7D_PLL_ENET_MAIN_25M_CLK>,
<&clks IMX7D_PLL_ENET_MAIN_250M_CLK>,
<&clks IMX7D_PLL_ENET_MAIN_100M_CLK>;
assigned-clock-rates = <0>, <0>, <0>, <100000000>, <250000000>;
phy-mode = "rgmii";
phy-handle = <ðphy0>;
phy-reset-gpios = <&gpio1 3 0>;
fsl,magic-packet;
status = "okay";
mdio {
#address-cells = <1>;
#size-cells = <0>;
ethphy0: ethernet-phy@1 { //here '@1' is the PHY address
compatible = "ethernet-phy-ieee802.3-c22";
reg = <1>;
};
};
};
Add the GPIO pin for the ENET_RESET_B
&iomuxc {
...
...
pinctrl_enet_reset: enet_resetgrp {
fsl,pins = <
MX7D_PAD_GPIO1_IO03__GPIO1_IO3 0x14 //ENET_RESET_B
>;
};
}
There is a PHY fixup in the arch/arm/mach-imx/<imx_cpu>.c
Here is the example in mach-imx7d.c
#define PHY_ID_KSZ9031 0x00221620
#define MICREL_PHY_ID_MASK 0x00fffff0
static void mmd_write_reg(struct phy_device *dev, int device, int reg, int val)
{
phy_write(dev, 0x0d, device);
phy_write(dev, 0x0e, reg);
phy_write(dev, 0x0d, (1 << 14) | device);
phy_write(dev, 0x0e, val);
}
static int ksz9031rn_phy_fixup(struct phy_device *dev)
{
/*
* min rx data delay, max rx/tx clock delay,
* min rx/tx control delay
*/
mmd_write_reg(dev, -1, 0x4, 0);
mmd_write_reg(dev, -1, 0x5, 0);
mmd_write_reg(dev, -1, 0x6, 0);
mmd_write_reg(dev, -1, 0x8, 0x003ff);
return 0;
}
static void __init imx7d_enet_phy_init(void)
{
if (IS_BUILTIN(CONFIG_PHYLIB)) {
phy_register_fixup_for_uid(PHY_ID_AR8031, 0xffffffff,
ar8031_phy_fixup);
phy_register_fixup_for_uid(PHY_ID_BCM54220, 0xffffffff,
bcm54220_phy_fixup);
phy_register_fixup_for_uid(PHY_ID_KSZ9031, MICREL_PHY_ID_MASK,
ksz9031rn_phy_fixup);
}
}
Now, the PHY is working on your board. :smileyhappy:
Reference: