当前位置: 首页 > article >正文

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恢复到工作状态。


http://www.kler.cn/news/365501.html

相关文章:

  • 鸿蒙next之导航组件跳转携带参数
  • 虚拟机网络设置为桥接模式
  • 代理与 Hubstudio 集成
  • 开发运维警示录-20241024
  • 【进阶OpenCV】 (19)-- Dlib库 --人脸表情识别
  • 3D、VR、AR技术的应用,对家电品牌营销有哪些影响?
  • 孤岛架构与微服务架构区别
  • 搜维尔科技:视觉映射灵巧手,五指灵巧手解决方案
  • Win/Mac/Android/iOS怎麼刪除代理設置?
  • #渗透测试#安全见闻7 硬件设备的网络安全问题与潜在漏洞分析
  • 【虚幻引擎UE】UE5 音频共振特效制作
  • 《Pyhon入门:07 map与filter函数的常用用法》
  • 命名空间std, using namespace std
  • django离散数学关系图谱答题推荐系统
  • windows msvc2017 x64编译AWS SDK CPP库
  • MacOS 使用ssh2-python报错ImportError: dlopen ... Library not loaded
  • 一文了解:多智能体系统(MAS)的演变(方法论篇)
  • 盘古信息:为制造企业打造全方位数字化转型方案
  • 创建ODBC数据源SQLConfigDataSource函数的用法
  • JavaEE——网络
  • Electron 是一个用于构建跨平台桌面应用程序的开源框架
  • java-实例化一个List,然后添加数据的方法详解
  • 云原生笔记
  • Find My平板键盘|苹果Find My技术与键盘结合,智能防丢,全球定位
  • 鸿蒙NEXT应用上架与分发步骤详解
  • 希尔排序的增量和缩小增量问题