PHY debugging experience

1. PHY debugging process

        1. Configure the correct PHY ADDR, PHY ID, clause 45 or 22 protocol in the device tree. Incorrect PHY ADDR configuration will cause MDC/MDIO communication to be abnormal or fail. The PHY ID is used to match the PHY driver.

        2. Read and write the PHY ID through MDC/MDIO and compare the PHY ID in the datasheet to confirm whether the MDC/MDIO communication is normal.

        3. When the MDC/MDIO communication is normal, read and write the registers of the PHY to check whether the status of the PHY is normal, such as master-slave, RGMII mode, and Link status.

        4. Confirm whether TXCLK is normal. 2.5MHZ corresponds to 10M speed, 25MHZ corresponds to 100M speed, and 125MHZ corresponds to 1000M speed. When the auto-negotiation fails, you can configure the fixed-link node in the device tree to force the MAC to work in a certain state , such as 100M full duplex.

fixed-link {
    speed = <100>;
	full-duplex;
};

        5. Check whether delay is configured on the MAC RGMII interface on the SOC side. If not, delay needs to be configured on the PHY side.

        6. Confirm whether the MAC on the SOC side is working normally, and confirm the RGMII mode, full-duplex/half-duplex mode, link status, and speed status through the devmem dump register information.

2. Description of phy driver development steps

        For the phy driver, it mainly implements two parts:

        1. Realize probe, remove, suspend, resume interfaces, used to complete the registration and deregistration of phy_driver, and the basis of the linux device-bus-driver model;

        2. Implement phy device processing related interfaces:

config_init
config_aneg
read_status
ack_interrupt
config_intr
did_interrupt
match_phy_device
ts_info
hwtstamp
rxtstamp
txtstamp
set_wol
get_wol

        2.1 Among them, config_init mainly performs some initialization configurations on the phy device;

        2.2 config_aneg and read_status are mainly used to set the self-adaptive mechanism of the phy device to obtain the state of the phy (mainly used to obtain the adaptation rate, duplex mode, etc.). Generally, these two interfaces can directly use genphy_config_aneg and genphy_read_status to trigger this phy device has a special definition;

        2.3 ack_interrupt, config_intr, and did_interrupt are mainly used for interrupt processing related to the link up/down of the phy device. This is related to the specific phy device, and you can check the corresponding chip manual for instructions;

        2.4 For ts_info, hwtstamp, rxtstamp, txtstamp, set_wol, get_wol are mainly timestamp-related processing, and most phy devices do not need to implement these interfaces.

        2.5 match_phy_device is mainly used to realize the matching detection between phy_device and phy_driver. If a phy_driver supports multiple types of phy_device, it is better to implement this interface. If the phy_driver is only suitable for a certain type of phy_device, it is not necessary to implement this interface. You need to set the supported phy_id and phy_mask.

        The above are the general steps to realize the phy driver. In general, if the phy device is not very special, it is not necessary to implement the phy driver. When mdiobus_register is used for devices that do not match the phy_driver, it will be bound to the genphy_driver, and the genphy_driver is basically For most phy devices, it can be driven normally. However, if we only need to perform some configurations in the probe interface of the phy driver (such as setting the led mode of the phy device, etc.), we can use the interfaces in genphy_driver for interfaces such as config_ange and read_status, and only implement the probe interface.

3. Claus45 read and write interface encapsulation

        SOC chip: Qualcomm does not support the C45 protocol by default

        PHY chip: marvell 88q1110, support register 13&14 extended control PCS/PMA register

        path:Linux/include/phy.h

static inline int phy_write_c45(struct phy_device *phydev, u32 dev_id, u32 regnum, u16 val)
{
        mdiobus_write(phydev-&gt;mdio.bus, phydev-&gt;mdio.addr, 0x0d, dev_id);
        mdiobus_write(phydev-&gt;mdio.bus, phydev-&gt;mdio.addr, 0x0e, regnum);
        mdiobus_write(phydev-&gt;mdio.bus, phydev-&gt;mdio.addr, 0x0d, 0x4000 | dev_id);
        return mdiobus_write(phydev-&gt;mdio.bus, phydev-&gt;mdio.addr, 0x0e, val);
}

static inline int phy_read_c45(struct phy_device *phydev, u16 dev_id, u32 regnum)
{       
        mdiobus_write(phydev-&gt;mdio.bus, phydev-&gt;mdio.addr, 0x0d, dev_id);
        mdiobus_write(phydev-&gt;mdio.bus, phydev-&gt;mdio.addr, 0x0e, regnum); 
        mdiobus_write(phydev-&gt;mdio.bus, phydev-&gt;mdio.addr, 0x0d, 0x4000 | dev_id);
        return mdiobus_read(phydev-&gt;mdio.bus, phydev-&gt;mdio.addr, 0x0e);
}

Guess you like

Origin blog.csdn.net/qq_41076734/article/details/129263072