英语

PHY子系统

作者:

Kishon Vijay Abraham I <kishon@ti.com>

本文档解释了通用PHY框架以及提供的API和使用方法。

简介

PHY 是物理层的缩写。 它用于将设备连接到物理介质,例如,USB控制器具有PHY以提供诸如串行化,解串行化,编码,解码等功能,并负责获得所需的数据传输速率。 请注意,某些USB控制器将PHY功能嵌入其中,而另一些则使用外部PHY。 使用PHY的其他外围设备包括无线局域网,以太网,SATA等。

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

该框架仅对使用外部PHY(PHY功能未嵌入控制器中)的设备有用。

注册/注销PHY提供程序

PHY提供程序是指实现一个或多个PHY实例的实体。 对于PHY提供程序仅实现PHY的单个实例的简单情况,该框架提供了of_phy_simple_xlate中of_xlate的自己的实现。 如果PHY提供程序实现多个实例,则应提供of_xlate的自己的实现。 of_xlate仅用于dt启动情况。

#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_register 和 devm_of_phy_provider_register 宏可用于注册 phy_provider,它将设备和of_xlate作为参数。 对于dt启动情况,所有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)

void devm_of_phy_provider_unregister(struct device *dev,
        struct phy_provider *phy_provider);
void of_phy_provider_unregister(struct phy_provider *phy_provider);

devm_of_phy_provider_unregister 和 of_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 ops来创建PHY。 phy_ops是一组函数指针,用于执行PHY操作,例如init,exit,power_on和power_off。

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

获取对PHY的引用

在控制器可以使用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_get, devm_phy_get 和 devm_phy_optional_get 可用于获取PHY。 在dt启动的情况下,字符串参数应包含dt数据中给出的phy名称,而在非dt启动的情况下,应包含PHY的标签。 两个devm_phy_get 在成功的PHY获取时,使用devres将设备与PHY关联。 在驱动程序分离时,将调用devres数据上的释放函数,并释放devres数据。 当phy是可选的时,应使用 _optional_get 变体。 这些函数永远不会返回 -ENODEV,而是在找不到phy时返回NULL。 一些通用驱动程序(例如ehci)可能会使用多个phy。 在这种情况下,可以使用 devm_of_phy_get 或 devm_of_phy_get_by_index 来基于名称或索引获取phy引用。

应该注意的是,NULL是有效的phy引用。 NULL phy上的所有phy使用者调用都变成NOP。 也就是说,释放调用,phy_init() 和 phy_exit() 调用,以及 phy_power_on() 和 phy_power_off() 调用在应用于NULL phy时都是NOP。 NULL phy对于处理可选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 运行时

此子系统已启用pm运行时。 因此,在创建PHY时,将调用此子系统创建的phy设备的 pm_runtime_enable,而在销毁PHY时,将调用 pm_runtime_disable。 请注意,此子系统创建的phy设备将是调用 phy_create 的设备的子设备(PHY提供程序设备)。

因此,由于父子关系,此子系统创建的phy_device的 pm_runtime_get_sync 将调用PHY提供程序设备的 pm_runtime_get_sync。 还应注意的是,phy_power_on 和 phy_power_off 分别执行 phy_pm_runtime_get_sync 和 phy_pm_runtime_put。 有一些导出的API,如 phy_pm_runtime_get,phy_pm_runtime_get_sync,phy_pm_runtime_put 和 phy_pm_runtime_put_sync 用于执行PM操作。

PHY映射

为了在没有DeviceTree的帮助下获得对PHY的引用,该框架提供了查找,可以将其与clkdev进行比较,后者允许将clk结构绑定到设备。 当struct 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);

DeviceTree绑定

有关PHY dt绑定的文档,请参见 @ Documentation/devicetree/bindings/phy/phy-bindings.txt