PHY 子系统 【ChatGPT】

发布时间 2023-12-10 19:39:26作者: 摩斯电码

PHY子系统

作者

本文档解释了通用PHY框架以及提供的API,以及如何使用。

简介

PHY是物理层的缩写。它用于将设备连接到物理介质,例如,USB控制器具有PHY来提供序列化、反序列化、编码、解码等功能,并负责获取所需的数据传输速率。需要注意的是,一些USB控制器内嵌了PHY功能,而其他一些使用外部PHY。其他使用PHY的外围设备包括无线局域网、以太网、SATA等。

创建此框架的目的是将分散在Linux内核中的PHY驱动程序整合到drivers/phy中,以增加代码重用性和提高代码可维护性。

此框架仅适用于使用外部PHY的设备(PHY功能未嵌入在控制器内部)。

注册/注销PHY提供者

PHY提供者是指实现一个或多个PHY实例的实体。对于简单情况,其中PHY提供者仅实现PHY的单个实例,框架提供了of_xlate的自己实现of_phy_simple_xlate。如果PHY提供者实现了多个实例,则应提供自己的of_xlate实现。of_xlate仅用于设备树引导情况。

#define of_phy_provider_register(dev, xlate)    \
        __of_phy_provider_register((dev), NULL, THIS_MODULE, (xlate))

#define devm_of_phy_provider_register(dev, xlate)       \
        __devm_of_phy_provider_register((dev), NULL, THIS_MODULE,
                                        (xlate))

of_phy_provider_registerdevm_of_phy_provider_register宏可用于注册phy_provider,它们接受设备和of_xlate作为参数。对于设备树引导情况,所有PHY提供者都应使用上述2个宏之一来注册PHY提供者。

与PHY提供者相关的设备树节点通常包含一组子节点,每个子节点代表一个单独的PHY。一些绑定可能在额外的级别中嵌套子节点以提供上下文和可扩展性,在这种情况下,可以使用低级别的of_phy_provider_register_full()devm_of_phy_provider_register_full()宏来覆盖包含子节点的节点。

#define of_phy_provider_register_full(dev, children, xlate) \
        __of_phy_provider_register(dev, children, THIS_MODULE, xlate)

#define devm_of_phy_provider_register_full(dev, children, xlate) \
        __devm_of_phy_provider_register_full(dev, children,
                                             THIS_MODULE, xlate)

devm_of_phy_provider_unregisterof_phy_provider_unregister可用于注销PHY。

创建PHY

PHY驱动程序应创建PHY,以便其他外围控制器可以使用它。PHY框架提供了2个API来创建PHY。

struct phy *phy_create(struct device *dev, struct device_node *node,
                       const struct phy_ops *ops);
struct phy *devm_phy_create(struct device *dev,
                            struct device_node *node,
                            const struct phy_ops *ops);

PHY驱动程序可以使用上述2个API之一来创建PHY,通过传递设备指针和PHY操作。phy_ops是一组用于执行PHY操作的函数指针,例如初始化、退出、上电和下电。

为了解引用私有数据(在phy_ops中),PHY提供者驱动程序可以在创建PHY后使用phy_set_drvdata(),并在phy_ops中使用phy_get_drvdata()来获取私有数据。

获取PHY的引用

在控制器可以使用PHY之前,它必须获取对其的引用。该框架提供以下API来获取对PHY的引用。

struct phy *phy_get(struct device *dev, const char *string);
struct phy *devm_phy_get(struct device *dev, const char *string);
struct phy *devm_phy_optional_get(struct device *dev,
                                  const char *string);
struct phy *devm_of_phy_get(struct device *dev, struct device_node *np,
                            const char *con_id);
struct phy *devm_of_phy_optional_get(struct device *dev,
                                     struct device_node *np,
                                     const char *con_id);
struct phy *devm_of_phy_get_by_index(struct device *dev,
                                     struct device_node *np,
                                     int index);

phy_getdevm_phy_getdevm_phy_optional_get可用于获取PHY。在设备树引导的情况下,字符串参数应包含设备树数据中给定的PHY名称,在非设备树引导的情况下,应包含PHY的标签。两个devm_phy_get使用devres将设备与PHY关联,成功获取PHY时会调用驱动程序分离时的释放函数,并释放devres数据。在PHY不可用时,_optional_get变体应该被使用,这些函数在PHY无法找到时不会返回-ENODEV,而是返回NULL。一些通用驱动程序,例如ehci,可能使用多个PHY。在这种情况下,可以使用devm_of_phy_getdevm_of_phy_get_by_index根据名称或索引获取PHY引用。

应该注意到,NULL是一个有效的PHY引用。对NULL PHY的所有PHY消费者调用都将成为NOP。也就是说,对NULL PHY的释放调用、phy_init()phy_exit()调用,以及phy_power_on()phy_power_off()调用在应用于NULL PHY时都是NOP。在处理可选PHY设备的设备中,NULL PHY是有用的。

API调用顺序

通常的调用顺序应该是:

[devm_][of_]phy_get()
phy_init()
phy_power_on()
[phy_set_mode_ext]
...
phy_power_off()
phy_exit()
[[of_]phy_put()]

一些PHY驱动程序可能不实现phy_init()phy_power_on(),但控制器应始终调用这些函数以与其他PHY兼容。一些PHY可能需要phy_set_mode,而其他可能使用默认模式(通常通过设备树或其他固件配置)。为了兼容性,如果知道将要使用的模式,应始终调用此函数。通常情况下,应在phy_power_on()之后调用此函数,尽管一些PHY驱动程序可能允许在任何时候调用它。

释放对PHY的引用

当控制器不再需要PHY时,必须释放在上述部分提到的API中获取的对PHY的引用。PHY框架提供了2个API来释放对PHY的引用。

void phy_put(struct phy *phy);
void devm_phy_put(struct device *dev, struct phy *phy);

这两个API用于释放对PHY的引用,devm_phy_put销毁与此PHY关联的devres。

销毁PHY

当创建PHY的驱动程序被卸载时,应使用以下2个API之一销毁所创建的PHY:

void phy_destroy(struct phy *phy);
void devm_phy_destroy(struct device *dev, struct phy *phy);

这两个API销毁PHY,devm_phy_destroy销毁与此PHY关联的devres。

PM Runtime

此子系统已启用pm runtime。因此,在创建PHY时,将调用此子系统创建的phy设备的pm_runtime_enable,而在销毁PHY时,将调用pm_runtime_disable。需要注意的是,此子系统创建的phy设备将是调用phy_create(PHY提供者设备)的设备的子设备。

因此,此子系统创建的phy设备的pm_runtime_get_sync将调用PHY提供者设备的pm_runtime_get_sync,因为它们之间存在父子关系。还应该注意的是,phy_power_onphy_power_off执行phy_pm_runtime_get_syncphy_pm_runtime_put。有一些导出的API,例如phy_pm_runtime_getphy_pm_runtime_get_syncphy_pm_runtime_putphy_pm_runtime_put_syncphy_pm_runtime_allowphy_pm_runtime_forbid,用于执行PM操作。

PHY映射

为了在没有设备树的帮助下获取对PHY的引用,该框架提供了查找功能,可以将其与允许将clk结构绑定到设备的clkdev进行比较。在运行时,可以进行查找,当已存在对结构体PHY的句柄时。

该框架提供了以下API来注册和注销查找:

int phy_create_lookup(struct phy *phy, const char *con_id,
                      const char *dev_id);
void phy_remove_lookup(struct phy *phy, const char *con_id,
                       const char *dev_id);

设备树绑定

PHY设备树绑定的文档可以在Documentation/devicetree/bindings/phy/phy-bindings.txt中找到。