phy初始化

在Linux系统中,PHY(物理层)设备通常用于处理网络接口的物理传输。初始化PHY设备和驱动其工作通常涉及Linux内核的PHY框架。以下是一般的流程和关键步骤:

 

1. PHY设备初始化

在Linux中,PHY设备的初始化主要由设备树配置、驱动程序加载以及内核的PHY子系统来处理。

 

设备树配置:PHY设备通常在设备树文件(*.dts文件)中定义,包含其地址和属性等信息。例如:

dts

复制代码

ethernet-phy@0 {

    reg = <0>;

    compatible = "ethernet-phy-idXXXX.XXXX";

};

设备树文件会将PHY地址和兼容性信息映射到驱动,从而使内核能够识别设备。

加载驱动:Linux内核的PHY驱动通常位于drivers/net/phy/目录下。通过内核配置选项(如CONFIG_PHYLIB)来启用PHY库,并在网络驱动中初始化PHY。

MDIO总线注册:MDIO(管理数据输入输出)是PHY芯片的标准管理接口。PHY驱动通过MDIO总线和设备通信,获取和设置PHY的状态。在Linux网络驱动中,初始化时会调用phy_connect等函数将PHY和MAC(媒体访问控制)层驱动绑定。

2. 配置和工作流程

PHY的初始化和工作流程通常由Linux内核的phy_device结构体和相关函数管理。

 

PHY扫描与连接:在驱动中,通过phy_connect函数扫描和连接PHY设备。该函数会搜索MDIO总线上的设备,并尝试匹配兼容的PHY驱动。

PHY状态管理:PHY设备状态机负责处理PHY的连接、断开、重启和功耗等状态。内核会调用phy_start函数启动PHY设备,之后在内核线程中定期检查设备的连接状态。

中断和状态检测:PHY设备支持基于中断或轮询的状态检测。当PHY检测到状态变化时,会通过中断或轮询的方式触发事件,内核将相应处理连接状态的变化。

速度和双工模式设置:PHY驱动还负责配置链路速度、双工模式等参数,通常由phy_ethtool_get_settings和phy_ethtool_set_settings函数处理,以适应链路需求。

3. 关键函数

在Linux内核代码中,以下几个函数是驱动PHY设备的关键:

 

phy_init_hw():初始化PHY硬件。

phy_connect():将PHY和网络设备连接。

phy_start():启动PHY,进入正常工作状态。

phy_stop():停止PHY设备,进入低功耗模式。

phy_polling_mode():启用轮询模式检测PHY状态。

phy_ethtool_get_settings() / phy_ethtool_set_settings():用于获取和设置PHY的链路参数。

示例流程

在驱动中,一般会这样调用PHY框架的API:

 

struct phy_device *phydev;

phydev = phy_connect(netdev, bus_id, &adjust_link, PHY_INTERFACE_MODE_GMII);

phy_start(phydev); // 启动PHY设备

其中,adjust_link是一个回调函数,用于更新链路状态。

 

通过实现phy_driver结构体和注册/注销函数来实现。以下是增强后的代码示例:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/phy.h>
#include <linux/netdevice.h>
#include <linux/ethtool.h>
#include <linux/platform_device.h>

static struct net_device *netdev;
static struct phy_device *phydev;
static struct mii_bus *mdio_bus;

// 定义PHY驱动结构体,描述支持的操作
static struct phy_driver my_phy_driver = {
    .phy_id         = 0x12345678,  // 替换为实际的PHY ID
    .phy_id_mask    = 0xffffffff,
    .name           = "My PHY Driver",
    .features       = PHY_BASIC_FEATURES,
    .config_init    = NULL,
    .config_aneg    = genphy_config_aneg,
    .read_status    = genphy_read_status,
    .suspend        = genphy_suspend,
    .resume         = genphy_resume,
};
// 自定义初始化函数,执行PHY特定的初始化配置
static int my_config_init(struct phy_device *phydev)
{
    int ret;

    // 示例:写入寄存器配置PHY特定功能
    ret = phy_write(phydev, 0x1F, 0x8000); // 写入寄存器0x1F
    if (ret < 0)
        return ret;

    printk(KERN_INFO "PHY initialized with custom configuration\n");
    return 0;
}

// 自定义自动协商配置函数
static int my_config_aneg(struct phy_device *phydev)
{
    int ret;

    // 开启自动协商
    ret = genphy_config_aneg(phydev); // 使用通用函数
    if (ret < 0)
        return ret;

    printk(KERN_INFO "PHY auto-negotiation configured\n");
    return 0;
}

// 自定义读取状态函数
static int my_read_status(struct phy_device *phydev)
{
    int ret;

    // 调用通用状态读取
    ret = genphy_read_status(phydev);
    if (ret < 0)
        return ret;

    if (phydev->link) {
        printk(KERN_INFO "PHY link up - %d Mbps %s\n",
               phydev->speed,
               phydev->duplex == DUPLEX_FULL ? "Full Duplex" : "Half Duplex");
    } else {
        printk(KERN_INFO "PHY link down\n");
    }
    return 0;
}

// 自定义挂起函数
static int my_suspend(struct phy_device *phydev)
{
    // 写入寄存器以实现PHY的低功耗模式
    phy_write(phydev, MII_BMCR, BMCR_PDOWN); // 进入断电模式
    printk(KERN_INFO "PHY suspended to low power mode\n");
    return 0;
}

// 自定义恢复函数
static int my_resume(struct phy_device *phydev)
{
    // 恢复PHY功耗模式
    phy_write(phydev, MII_BMCR, 0); // 退出断电模式
    printk(KERN_INFO "PHY resumed from low power mode\n");
    return 0;
}


// 连接PHY设备时的回调函数
static void my_adjust_link(struct net_device *dev)
{
    struct phy_device *phydev = dev->phydev;

    if (phydev->link) {
        printk(KERN_INFO "PHY: Link is up - %d Mbps %s\n",
               phydev->speed,
               phydev->duplex == DUPLEX_FULL ? "Full Duplex" : "Half Duplex");
    } else {
        printk(KERN_INFO "PHY: Link is down\n");
    }
}

// 扫描并注册MDIO总线上的PHY设备
static int my_scan_and_register_phy(void)
{
    int i;
    struct phy_device *phy;

    for (i = 0; i < PHY_MAX_ADDR; i++) {
        phy = mdiobus_get_phy(mdio_bus, i);
        if (phy) {
            printk(KERN_INFO "Found PHY at address %d\n", i);
            // 注册找到的PHY设备
            if (!try_module_get(phy->mdio.dev.driver->owner)) {
                printk(KERN_ERR "Failed to get module for PHY at address %d\n", i);
                continue;
            }
            // 连接PHY设备
            phydev = phy_connect(netdev, phy_name(phy), &my_adjust_link, PHY_INTERFACE_MODE_RGMII);
            if (IS_ERR(phydev)) {
                printk(KERN_ERR "Failed to connect PHY at address %d\n", i);
                module_put(phy->mdio.dev.driver->owner);
                continue;
            }
            phydev->supported &= PHY_BASIC_FEATURES;
            phydev->advertising = phydev->supported;
            // 启动PHY设备
            phy_start(phydev);
            printk(KERN_INFO "PHY device initialized and started\n");
            return 0;
        }
    }
    printk(KERN_ERR "No PHY device found\n");
    return -ENODEV;
}

static int __init my_phy_driver_init(void)
{
    int ret;

    // 注册PHY驱动
    ret = phy_driver_register(&my_phy_driver, THIS_MODULE);
    if (ret) {
        printk(KERN_ERR "Failed to register PHY driver\n");
        return ret;
    }

    // 分配并初始化MDIO总线
    mdio_bus = mdiobus_alloc();
    if (!mdio_bus) {
        printk(KERN_ERR "Failed to allocate MDIO bus\n");
        phy_driver_unregister(&my_phy_driver);
        return -ENOMEM;
    }
    snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "mdio_bus-0");

    ret = mdiobus_register(mdio_bus);
    if (ret) {
        printk(KERN_ERR "Failed to register MDIO bus\n");
        mdiobus_free(mdio_bus);
        phy_driver_unregister(&my_phy_driver);
        return ret;
    }

    // 创建网络设备
    netdev = alloc_etherdev(0);
    if (!netdev) {
        printk(KERN_ERR "Failed to allocate net device\n");
        mdiobus_unregister(mdio_bus);
        mdiobus_free(mdio_bus);
        phy_driver_unregister(&my_phy_driver);
        return -ENOMEM;
    }

    // 扫描并连接MDIO总线上的PHY设备
    ret = my_scan_and_register_phy();
    if (ret) {
        printk(KERN_ERR "No suitable PHY device found or failed to register PHY\n");
        free_netdev(netdev);
        mdiobus_unregister(mdio_bus);
        mdiobus_free(mdio_bus);
        phy_driver_unregister(&my_phy_driver);
        return ret;
    }



    return 0;
}

static void __exit my_phy_driver_exit(void)
{
    phy_disconnect(phydev);
    free_netdev(netdev);
    mdiobus_unregister(mdio_bus);
    mdiobus_free(mdio_bus);
    phy_driver_unregister(&my_phy_driver);
    printk(KERN_INFO "PHY device driver unloaded\n");
}

module_init(my_phy_driver_init);
module_exit(my_phy_driver_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("PHY device initialization with scanning and driver registration demo");

 

代码说明

  1. MDIO总线的注册和初始化:分配并注册MDIO总线,用于与PHY设备通信。
  2. 网络设备的创建:分配网络设备(如以太网接口)。
  3. PHY设备连接:通过phy_connect函数连接PHY设备,指定回调函数my_adjust_link处理链路状态变化。
  4. PHY设备配置与启动:设置PHY的支持模式和配置,然后使用phy_start启动PHY设备。
  • PHY驱动结构体 (phy_driver):定义了PHY设备的基本信息和回调函数,例如config_initconfig_anegread_status等。这些函数用于初始化配置、管理状态、挂起和恢复PHY设备。

    • phy_id:用于匹配设备的PHY ID。
    • config_aneg:配置自动协商。
    • read_status:读取设备状态,这里使用了通用的genphy_read_status
  • PHY驱动注册 (phy_driver_register):在初始化时注册PHY驱动,并在卸载时取消注册。

  • 状态机检查read_status回调将读取PHY的状态,并在链路状态变化时更新状态机。

  • 扫描PHY设备:在my_scan_and_register_phy函数中,通过mdiobus_get_phy()函数扫描MDIO总线的每个地址(通常为0到31),检查是否存在可用的PHY设备,并尝试连接。

  • 注册找到的PHY设备:如果找到了PHY设备,通过try_module_get()来确保该设备的驱动模块被引用,然后使用phy_connect()连接到网络设备,并配置PHY的支持模式。

  • 错误处理:在扫描和注册过程中,加入了错误处理逻辑,确保在没有找到PHY设备或连接失败时,能够清理资源并退出。

  • 代码自定义部分说明

  • my_config_init:在config_init回调中可以添加PHY的特定初始化配置,比如写入特定寄存器以启用特定功能。在本例中,写入寄存器0x1F来执行初始化配置。

  • my_config_aneg:配置自动协商功能,通常使用通用的genphy_config_aneg即可,同时可以在该函数中扩展或自定义自动协商的细节。

  • my_read_status:自定义状态读取函数。此函数读取PHY的链路状态,并打印当前链路的速度和双工模式。这一部分通常是通过调用genphy_read_status来获取当前状态。

  • my_suspendmy_resume:挂起和恢复函数。在挂起时写入BMCR_PDOWN以进入低功耗模式,在恢复时退出低功耗模式,确保PHY恢复到工作状态。

 

### PHY Initialization in Network Devices In network devices, particularly those involving Ethernet interfaces like the one described with an Allwinner T113-i processor[^3], initializing the Physical Layer (PHY) is crucial for establishing reliable communication over physical media such as twisted pair cables or fiber optics. #### Understanding PHY Configuration Space The configuration space within a network device includes registers that control various aspects of operation including speed settings, duplex mode, and auto-negotiation features. For instance, when configuring an Ethernet interface, specific bits within these registers dictate whether full-duplex or half-duplex modes are enabled along with supported speeds (e.g., 10 Mbps, 100 Mbps). ```c // Example pseudo-code to configure PHY register via MDIO bus void phy_configure(int mdio_addr, int reg_num, uint16_t value){ // Write 'value' into specified PHY register using MDIO protocol } ``` #### Auto-Negotiation Process Auto-negotiation allows two connected devices to exchange capabilities through Fast Link Pulses (FLPs). This process determines mutual link parameters automatically without manual intervention by users. Once both ends agree upon optimal settings, they proceed accordingly while maintaining backward compatibility where necessary. Once initialized properly, the driver software interacts closely with this hardware component ensuring proper setup before enabling higher-level protocols stack operations commence effectively transmitting/receiving packets across networks seamlessly. #### Initializing the PHY Device Initialization typically involves resetting the PHY chip followed by setting up its operational parameters according to system requirements: ```bash ethtool -s eth0 advertise 0x0F autoneg off ifconfig eth0 down mii-tool -R eth0 # Reset MII management state machine. sleep 1 # Wait briefly after reset command completes execution. ifconfig eth0 up # Bring back online once reconfigured successfully. ``` This sequence ensures any previous configurations do not interfere during new setups thus providing clean slate conditions essential for successful negotiations between peers at either end points involved in communications sessions established thereafter.
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值