首页 > 其他分享 >phy初始化

phy初始化

时间:2024-10-27 10:16:59浏览次数:3  
标签:初始化 driver PHY ret phydev phy my

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

标签:初始化,driver,PHY,ret,phydev,phy,my
From: https://blog.csdn.net/scm06111/article/details/143233304

相关文章

  • 多个phy设备同时工作
    在Linux系统中支持多个PHY设备同时工作,通常需要为每个PHY设备分配不同的MAC控制器接口或网卡设备,并且配置适当的网络协议栈和交换逻辑。以下是实现多个PHY设备同时工作的关键步骤和注意事项。###1.设备树配置设备树中,定义多个PHY设备并将它们连接到不同的MAC控制器(或网卡接......
  • phy驱动开发必备
     PHY驱动调试涉及到物理层接口驱动程序的开发和故障排除,通常应用于网络通信设备,如以太网、光纤通信设备等。在调试过程中,主要关注以下几个方面: 1.PHY驱动基本概念PHY(PhysicalLayer):物理层负责数据的物理传输,PHY芯片用于处理信号编码、解码、传输等底层操作。MAC(MediaA......
  • (自用)C语言字符串初始化““和\0的问题
    chars[10]="";正常。应该是全部初始化为\0了?printf会输出空白 chars[10]='';报错。【带引号的字符串应至少包含一个字符】【空字符常量】【“初始化”:无法从“char”转换为“char[10]”】 chars[10]='0';报错。【应使用“{…}”初始化聚合对象】【“初始化”......
  • sentinel-SPI初始化时机
    时机一引入alibaba-starter-sentinel如果使用了alibaba-starter-sentinel则不需要手动调用因为com.alibaba.cloud.sentinel.custom.SentinelAutoConfiguration#init在这里面执行了自动调用@PostConstructprivatevoidinit(){if(StringUtils.isEmpty(System.ge......
  • MCU初始化
    1.MCU模块初始化配置MCU模块中配置了一个default时钟Mcu_Init(&Mcu_Config[0])-->(void)Mcu_InitClock_Arch(MCU_API_SERVICE_INIT,&(ConfigPtr->McuClockConfigPtr[ConfigPtr->McuDefaultClockSettingId])在这里会初始化一次时钟配置在Ecum中配置了MCU初始化。会提示配......
  • 磁盘的初始化
    ......
  • 《A Spatiotemporal Fusion Transformer Model for Chlorophyll-a Concentrations Pre
    研究背景论文研究了叶绿素-a(Chla)的预测,这是海洋生态系统健康和环境变化的重要指标。传统的物理模型和数据驱动模型在Chla预测上存在局限性,尤其是长时间序列预测和大面积预测。深度学习方法近年来得到了关注,但大多仅能实现短期预测,且难以有效提取时空依赖性。研究目标......
  • C语言中的初始化是什么意思
    在C语言中,初始化是指在定义变量时为其赋予初值的过程。通过初始化,可以确保变量在使用之前具有已知的初始值,避免了未初始化变量的不确定行为。初始化可以在变量定义时直接赋值,也可以通过赋予默认值或调用特定的初始化函数来完成。C语言中的初始化在C语言中,初始化是指在定义变......
  • Unity Physics.Raycast发射一条射线并检测它与场景中物体的碰撞
    在Unity中,Physics.Raycast是一种非常常用的物理检测方法,用于发射一条射线并检测它与场景中物体的碰撞。这种方法在许多游戏场景中非常重要,例如用于射击、检测地面、触发事件等。1.基本概念射线(Ray):在三维空间中,射线是一个从某一点出发并沿着某个方向延伸的无穷长线。碰撞......
  • (环境篇日志-CVPR2024 ) Physical 3D Adversarial Attacks against Monocular Depth E
    题目:Physical3DAdversarialAttacksagainstMonocularDepthEstimationinAutonomousDriving作者:JunhaoZheng,ChenhaoLin*,JiahaoSun,ZhengyuZhao,QianLi,ChaoShen*单位:Xi’anJiaotongUniversity收录:CVPR2024论文:[Physical3DAdversarialAttacks......