PHY子系统
作者
- Kishon Vijay Abraham I [email protected]
本文档解释了通用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_register
和devm_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_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,通过传递设备指针和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_get
、devm_phy_get
和devm_phy_optional_get
可用于获取PHY。在设备树引导的情况下,字符串参数应包含设备树数据中给定的PHY名称,在非设备树引导的情况下,应包含PHY的标签。两个devm_phy_get
使用devres将设备与PHY关联,成功获取PHY时会调用驱动程序分离时的释放函数,并释放devres数据。在PHY不可用时,_optional_get变体应该被使用,这些函数在PHY无法找到时不会返回-ENODEV,而是返回NULL。一些通用驱动程序,例如ehci,可能使用多个PHY。在这种情况下,可以使用devm_of_phy_get
或devm_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_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
、phy_pm_runtime_allow
和phy_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
中找到。