PMBus 核心驱动程序和内部 API¶
简介¶
[来自 pmbus.org] 电源管理总线 (PMBus) 是一种开放标准电源管理协议,具有完全定义的命令语言,有助于与电源转换器和电源系统中的其他设备进行通信。 该协议通过行业标准 SMBus 串行接口实现,可对兼容电源转换产品进行编程、控制和实时监控。 这种灵活且高度通用的标准允许基于模拟和数字技术的设备之间进行通信,并提供真正的互操作性,从而降低设计复杂性并缩短电源系统设计人员的上市时间。 该开放式电源系统标准由领先的电源和半导体公司率先推出,由 PMBus 实施者论坛 (PMBus-IF) 维护和推广,该论坛由 30 多个采用者组成,旨在为用户提供支持并促进采用。
不幸的是,虽然 PMBus 命令是标准化的,但没有强制性命令,制造商可以根据需要添加任意数量的非标准命令。 此外,如果执行不支持的命令,不同的 PMBu 设备的行为也不同。 有些设备返回错误,有些设备返回 0xff 或 0xffff 并设置状态错误标志,有些设备可能只是挂起。
尽管存在所有这些困难,但自内核版本 2.6.39 以来,通用 PMBus 设备驱动程序仍然有用并受到支持。 但是,除了核心 PMBus 驱动程序之外,还需要支持设备特定的扩展,因为人们根本不知道 PMBus 设备开发人员接下来会想出什么新的设备特定功能。
为了使设备特定的扩展尽可能具有可扩展性,并避免必须为新设备重复修改核心 PMBus 驱动程序,PMBus 驱动程序被拆分为核心、通用和设备特定代码。 核心代码(在 pmbus_core.c 中)提供通用功能。 通用代码(在 pmbus.c 中)为通用 PMBus 设备提供支持。 设备特定代码负责设备特定的初始化,并在需要时将设备特定功能映射到通用功能。 这在某种程度上与 PCI 代码类似,其中通用代码根据需要通过各种设备的怪癖进行增强。
PMBus 设备功能自动检测¶
对于通用 PMBus 设备,pmbus.c 中的代码尝试自动检测所有受支持的 PMBus 命令。 自动检测受到一定的限制,因为需要考虑的变量太多。 例如,几乎不可能自动检测哪些 PMBus 命令已分页,哪些命令已跨所有页面复制(有关多页 PMBus 设备的详细信息,请参阅 PMBus 规范)。
因此,如果并非所有命令都可以自动检测到,则提供设备特定的驱动程序通常是有意义的。 此驱动程序中的数据结构可用于通知核心驱动程序有关各个芯片支持的功能。
某些命令始终会自动检测到。 这适用于所有限制命令(lcrit、min、max 和 crit 属性)以及关联的警报属性。 限制和警报属性是自动检测到的,因为可能存在的组合太多,无法提供手动配置界面。
PMBus 内部 API¶
核心和设备特定 PMBus 代码之间的 API 在 drivers/hwmon/pmbus/pmbus.h 中定义。 除了内部 API 之外,pmbus.h 还定义了标准 PMBus 命令和虚拟 PMBus 命令。
标准 PMBus 命令¶
标准 PMBus 命令(命令值 0x00 到 0xff)在 PMBUs 规范中定义。
虚拟 PMBus 命令¶
提供虚拟 PMBus 命令是为了支持几个芯片供应商已经实现的非标准功能,因此希望提供支持。
虚拟 PMBus 命令以命令值 0x100 开头,因此可以很容易地与标准 PMBus 命令(不能有大于 0xff 的值)区分开来。 对虚拟 PMBus 命令的支持是设备特定的,因此必须在设备特定的代码中实现。
虚拟命令名为 PMBUS_VIRT_xxx,并以 PMBUS_VIRT_BASE 开头。 所有虚拟命令都是字大小。
目前有两种类型的虚拟命令。
READ 命令是只读的;写入将被忽略或返回错误。
RESET 命令是读/写的。 读取重置寄存器返回零(用于检测),写入任何值都会导致关联的历史记录被重置。
虚拟命令必须在设备特定的驱动程序代码中处理。 如果支持虚拟命令,芯片驱动程序代码将返回非负值,如果不支持虚拟命令,则返回负错误代码。 在这种情况下,芯片驱动程序可能会返回 -ENODATA 或任何其他 Linux 错误代码,但处理非 -ENODATA 的错误代码效率更高,因此首选。 无论哪种情况,如果芯片驱动程序在读取或写入虚拟寄存器时返回错误代码,则调用的 PMBus 核心代码将中止(换句话说,PMBus 核心代码永远不会将虚拟命令发送到芯片)。
PMBus 驱动程序信息¶
PMBus 驱动程序信息(在 struct pmbus_driver_info 中定义)是设备特定驱动程序将信息传递给核心 PMBus 驱动程序的主要手段。 具体来说,它提供以下信息。
对于支持以直接数据格式存储其数据的设备,它提供用于将寄存器值转换为标准化数据的系数。 此数据通常由芯片制造商在设备数据表中提供。
可以将支持的芯片功能提供给核心驱动程序。 对于如果执行不支持的命令会产生不良反应的芯片,和/或为了加快设备检测和初始化速度,这可能是必要的。
提供几个函数入口点,以支持覆盖和/或增强通用命令执行。 此功能可用于将非标准 PMBus 命令映射到标准命令,或使用设备特定信息增强标准命令返回值。
PEC 支持¶
许多 PMBus 设备支持 SMBus PEC(数据包错误检查)。 如果 I2C 适配器和 PMBus 芯片都支持,则默认情况下启用它。 如果支持 PEC,则 PMBus 核心驱动程序会将名为“pec”的属性添加到 I2C 设备。 此属性可用于控制与 PMBus 芯片通信中的 PEC 支持。
API 函数¶
芯片驱动程序提供的函数¶
如果成功,所有函数都返回命令返回值(读取)或零(写入)。 返回值 -ENODATA 表示不存在制造商特定的命令,但可能存在标准 PMBus 命令。 任何其他负返回值表示该芯片不存在该命令,并且不应尝试读取或写入标准命令。
如上所述,此规则的一个例外适用于虚拟命令,虚拟命令必须在驱动程序特定的代码中处理。 有关更多详细信息,请参阅上面的“虚拟 PMBus 命令”。
核心 PMBus 驱动程序代码中的命令执行如下
if (chip_access_function) {
status = chip_access_function();
if (status != -ENODATA)
return status;
}
if (command >= PMBUS_VIRT_BASE) /* For word commands/registers only */
return -EINVAL;
return generic_access();
芯片驱动程序可能会在 struct pmbus_driver_info 中提供指向以下函数的指针。 所有函数都是可选的。
int (*read_byte_data)(struct i2c_client *client, int page, int reg);
从页面 <page>、寄存器 <reg> 读取字节。 <page> 可以为 -1,表示“当前页面”。
int (*read_word_data)(struct i2c_client *client, int page, int phase,
int reg);
从页面 <page>、阶段 <phase>、寄存器 <reg> 读取字。 如果芯片不支持多个阶段,则可以忽略 phase 参数。 如果芯片支持多个阶段,则 phase 值 0xff 表示所有阶段。
int (*write_word_data)(struct i2c_client *client, int page, int reg,
u16 word);
将字写入页面 <page>、寄存器 <reg>。
int (*write_byte)(struct i2c_client *client, int page, u8 value);
将字节写入页面 <page>、寄存器 <reg>。 <page> 可以为 -1,表示“当前页面”。
int (*identify)(struct i2c_client *client, struct pmbus_driver_info *info);
确定支持的 PMBus 功能。 只有在芯片驱动程序支持多个芯片并且芯片功能不是预先确定的情况下,此函数才是必要的。 它目前仅由通用 pmbus 驱动程序 (pmbus.c) 使用。
核心驱动程序导出的函数¶
预计芯片驱动程序使用以下函数来读取或写入 PMBus 寄存器。 芯片驱动程序也可以使用直接 I2C 命令。 如果使用直接 I2C 命令,则芯片驱动程序代码不得直接修改当前页面,因为所选页面缓存在核心驱动程序中,并且核心驱动程序将假定它已被选中。 必须使用 pmbus_set_page() 选择新页面。
int pmbus_set_page(struct i2c_client *client, u8 page, u8 phase);
将 PMBus 页面寄存器设置为 <page> 和 <phase> 以供后续命令使用。 如果芯片不支持多个阶段,则忽略 phase 参数。 否则,phase 值 0xff 选择所有阶段。
int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 phase,
u8 reg);
从 <page>、<phase>、<reg> 读取字数据。 类似于 i2c_smbus_read_word_data()
,但首先选择页面和阶段。 如果芯片不支持多个阶段,则忽略 phase 参数。 否则,phase 值 0xff 选择所有阶段。
int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg,
u16 word);
将字数据写入 <page>、<reg>。 类似于 i2c_smbus_write_word_data()
,但首先选择页面。
int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg);
从 <page>、<reg> 读取字节数据。 类似于 i2c_smbus_read_byte_data()
,但首先选择页面。 <page> 可以为 -1,表示“当前页面”。
int pmbus_write_byte(struct i2c_client *client, int page, u8 value);
将字节数据写入 <page>、<reg>。 类似于 i2c_smbus_write_byte()
,但首先选择页面。 <page> 可以为 -1,表示“当前页面”。
void pmbus_clear_faults(struct i2c_client *client);
在所有芯片页面上执行 PMBus“清除故障”命令。 如果定义了设备特定的 write_byte 函数,则此函数会调用该函数。 因此,_不能_从该函数调用它。
bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg);
检查字节寄存器是否存在。 如果寄存器存在,则返回 true,否则返回 false。 如果定义了设备特定的 write_byte 函数以获取芯片状态,则此函数会调用该函数。 因此,_不能_从该函数调用它。
bool pmbus_check_word_register(struct i2c_client *client, int page, int reg);
检查字寄存器是否存在。 如果寄存器存在,则返回 true,否则返回 false。 如果定义了设备特定的 write_byte 函数以获取芯片状态,则此函数会调用该函数。 因此,_不能_从该函数调用它。
int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info);
执行探测函数。 类似于其他驱动程序的标准探测函数,其中指向 struct pmbus_driver_info 的指针作为附加参数。 如果支持,则调用 identify 函数。 必须仅从设备探测函数调用。
const struct pmbus_driver_info
*pmbus_get_driver_info(struct i2c_client *client);
返回指向传递给 pmbus_do_probe() 的 struct pmbus_driver_info 的指针。
PMBus 驱动程序平台数据¶
PMBus 平台数据在 include/linux/pmbus.h 中定义。 平台数据当前提供一个带有四个已用位的标志字段
#define PMBUS_SKIP_STATUS_CHECK BIT(0)
#define PMBUS_WRITE_PROTECTED BIT(1)
#define PMBUS_NO_CAPABILITY BIT(2)
#define PMBUS_READ_STATUS_AFTER_FAILED_CHECK BIT(3)
#define PMBUS_NO_WRITE_PROTECT BIT(4)
#define PMBUS_USE_COEFFICIENTS_CMD BIT(5)
#define PMBUS_OP_PROTECTED BIT(6)
#define PMBUS_VOUT_PROTECTED BIT(7)
struct pmbus_platform_data {
u32 flags; /* Device specific flags */
/* regulator support */
int num_regulators;
struct regulator_init_data *reg_init_data;
};
标志¶
PMBUS_SKIP_STATUS_CHECK
在寄存器检测期间,跳过检查状态寄存器是否存在通信或命令错误。
某些 PMBus 芯片在尝试读取不受支持的寄存器时会响应有效数据。 对于此类芯片,尝试确定芯片寄存器是否存在时,必须检查状态寄存器。 其他 PMBus 芯片不支持 STATUS_CML 寄存器,或者报告没有明显原因的通信错误。 对于此类芯片,必须禁用状态寄存器检查。
某些 i2c 控制器不支持单字节命令(没有数据的写命令,i2c_smbus_write_byte()
)。 对于此类控制器,无法清除状态寄存器,并且必须设置 PMBUS_SKIP_STATUS_CHECK 标志。
PMBUS_WRITE_PROTECTED
如果芯片受到写保护并且写保护不是由标准 WRITE_PROTECT 命令确定的,则设置此标志。
PMBUS_NO_CAPABILITY
某些 PMBus 芯片在读取 CAPABILITY 寄存器时不会响应有效数据。 对于此类芯片,应设置此标志,以便 PMBus 核心驱动程序不使用 CAPABILITY 来确定其行为。
PMBUS_READ_STATUS_AFTER_FAILED_CHECK
在每次寄存器检查失败后读取 STATUS 寄存器。
某些 PMBus 芯片在尝试读取不受支持的寄存器时最终会进入未定义的状态。 对于此类芯片,必须在寄存器检查失败后将芯片 pmbus 控制器重置为已知状态。 这可以通过读取已知寄存器来完成。 通过设置此标志,驱动程序将在每次寄存器检查失败后尝试读取 STATUS 寄存器。 此读取可能会失败,但它会将芯片置于已知状态。
PMBUS_NO_WRITE_PROTECT
某些 PMBus 芯片在读取 WRITE_PROTECT 寄存器时会响应无效数据。 对于此类芯片,应设置此标志,以便 PMBus 核心驱动程序不使用 WRITE_PROTECT 命令来确定其行为。
PMBUS_USE_COEFFICIENTS_CMD
设置此标志后,PMBus 核心驱动程序将使用 COEFFICIENTS 寄存器来初始化直接模式格式的系数。
PMBUS_OP_PROTECTED
如果芯片 OPERATION 命令受到保护并且保护不是由标准 WRITE_PROTECT 命令确定的,则设置此标志。
PMBUS_VOUT_PROTECTED
如果芯片 VOUT_COMMAND 命令受到保护并且保护不是由标准 WRITE_PROTECT 命令确定的,则设置此标志。
模块参数¶
pmbus_core.wp: PMBus 写保护强制模式
PMBus 可能会提供各种写保护配置。 如果需要特定的写保护,可以使用“pmbus_core.wp”。 实际更改保护的能力也可能取决于芯片,因此实际的运行时写保护配置可能与请求的配置不同。 pmbus_core 当前支持以下值
0:删除写保护。
1:禁用所有写入,但写入 WRITE_PROTECT、OPERATION、PAGE、ON_OFF_CONFIG 和 VOUT_COMMAND 命令除外。
2:禁用所有写入,但写入 WRITE_PROTECT、OPERATION 和 PAGE 命令除外。
3:禁用所有写入,但写入 WRITE_PROTECT 命令除外。 请注意,保护应包括 PAGE 寄存器。 如果芯片严格遵循 PMBus 规范,这可能会给多页芯片带来问题,从而阻止芯片更改活动页面。