稳压器消费者驱动接口

本文描述了消费者设备驱动的稳压器接口。有关本文中使用的术语的描述,请参阅 Linux 电压和电流稳压器框架

1. 消费者稳压器访问(静态和动态驱动)

消费者驱动可以通过调用以下函数来访问其电源稳压器:

regulator = regulator_get(dev, "Vcc");

消费者传入其 struct device 指针和电源 ID。然后,核心通过查阅特定于机器的查找表来找到正确的稳压器。如果查找成功,则此调用将返回指向为该消费者供电的 struct regulator 的指针。

要释放稳压器,消费者驱动应调用:

regulator_put(regulator);

消费者可以由多个稳压器供电,例如,具有模拟和数字电源的编解码器消费者。

digital = regulator_get(dev, "Vcc");  /* digital core */
analog = regulator_get(dev, "Avdd");  /* analog */

稳压器访问函数 regulator_get()regulator_put() 通常会在设备驱动的 probe() 和 remove() 函数中分别调用。

2. 稳压器输出使能和禁用(静态和动态驱动)

消费者可以通过调用以下函数来使能其电源:

int regulator_enable(regulator);
注意

在调用 regulator_enable() 之前,电源可能已经被使能。如果消费者共享稳压器,或者稳压器先前已由引导加载程序或内核板初始化代码使能,则可能会发生这种情况。

消费者可以通过调用以下函数来确定稳压器是否已使能:

int regulator_is_enabled(regulator);

当稳压器已使能时,这将返回 > 0。

当不再需要时,消费者可以通过调用以下函数来禁用其电源:

int regulator_disable(regulator);
注意

如果它与其他消费者共享,则可能不会禁用电源。只有当使能引用计数为零时,才会禁用稳压器。

最后,在紧急情况下可以强制禁用稳压器:

int regulator_force_disable(regulator);
注意

这将立即并强制关闭稳压器输出。所有消费者都将断电。

3. 稳压器电压控制和状态(动态驱动)

一些消费者驱动程序需要能够动态更改其电源电压,以匹配系统工作点。例如,CPUfreq 驱动程序可以根据频率缩放电压以节省功耗,SD 驱动程序可能需要选择正确的卡电压等。

消费者可以通过调用以下函数来控制其电源电压:

int regulator_set_voltage(regulator, min_uV, max_uV);

其中 min_uV 和 max_uV 是以微伏为单位的最小和最大可接受电压。

注意:这可以在稳压器已使能或禁用时调用。如果在使能时调用,则电压会立即变化,否则电压配置会发生变化,并且电压会在下次使能稳压器时物理设置。

可以通过调用以下函数来找到稳压器配置的电压输出:

int regulator_get_voltage(regulator);
注意

get_voltage() 将返回配置的输出电压,无论稳压器是否已使能或禁用,并且不应用于确定稳压器输出状态。但是,它可以与 is_enabled() 结合使用以确定稳压器的物理输出电压。

4. 稳压器电流限制控制和状态(动态驱动)

一些消费者驱动程序需要能够动态更改其电源电流限制,以匹配系统工作点。例如,LCD 背光驱动程序可以更改电流限制以改变背光亮度,USB 驱动程序可能希望在供电时将限制设置为 500mA。

消费者可以通过调用以下函数来控制其电源电流限制:

int regulator_set_current_limit(regulator, min_uA, max_uA);

其中 min_uA 和 max_uA 是以微安为单位的最小和最大可接受电流限制。

注意

这可以在稳压器已使能或禁用时调用。如果在使能时调用,则电流限制会立即变化,否则电流限制配置会发生变化,并且电流限制会在下次使能稳压器时物理设置。

可以通过调用以下函数来找到稳压器的电流限制:

int regulator_get_current_limit(regulator);
注意

get_current_limit() 将返回电流限制,无论稳压器是否已使能或禁用,并且不应用于确定稳压器电流负载。

5. 稳压器工作模式控制和状态(动态驱动)

当消费者工作状态发生变化时,一些消费者可以通过更改其电源稳压器的工作模式来进一步节省系统功耗,使其更有效率。例如,消费者驱动程序处于空闲状态,随后消耗的电流较少。

稳压器工作模式可以间接或直接更改。

间接工作模式控制。

消费者驱动程序可以通过调用以下函数来请求更改其电源稳压器工作模式:

int regulator_set_load(struct regulator *regulator, int load_uA);

这将导致核心重新计算稳压器上的总负载(基于其所有消费者),并更改工作模式(如果必要且允许)以最佳匹配当前工作负载。

load_uA 值可以从消费者的数据表中确定。例如,大多数数据表都有表格显示在某些情况下消耗的最大电流。

大多数消费者将使用间接工作模式控制,因为他们不了解稳压器,也不知道稳压器是否与其他消费者共享。

直接工作模式控制。

定制或紧密耦合的驱动程序可能希望根据其工作点直接控制稳压器工作模式。这可以通过调用以下函数来实现:

int regulator_set_mode(struct regulator *regulator, unsigned int mode);
unsigned int regulator_get_mode(struct regulator *regulator);

直接模式将仅由了解稳压器且不与其他消费者共享稳压器的消费者使用。

6. 稳压器事件

稳压器可以通知消费者外部事件。消费者可以在稳压器处于压力或故障条件下接收事件。

消费者可以通过调用以下函数来注册对稳压器事件的兴趣:

int regulator_register_notifier(struct regulator *regulator,
                                struct notifier_block *nb);

消费者可以通过调用以下函数来取消注册兴趣:

int regulator_unregister_notifier(struct regulator *regulator,
                                  struct notifier_block *nb);

稳压器使用内核通知器框架向其感兴趣的消费者发送事件。

7. 稳压器直接寄存器访问

某些类型的电源管理硬件或固件的设计方式是,它们需要对稳压器进行低级硬件访问,而无需内核的参与。此类设备的示例包括:

  • 具有压控振荡器的时钟源和通过 I2C 改变电源电压以实现所需输出时钟频率的控制逻辑

  • 热管理固件,可以在过热条件下发出任意 I2C 事务来执行系统断电

要设置此类设备/固件,需要为其配置各种参数,例如稳压器的 I2C 地址、各种稳压器寄存器的地址等。稳压器框架提供了以下帮助程序来查询这些详细信息。

特定于总线的详细信息,例如 I2C 地址或传输速率,由 regmap 框架处理。要获取稳压器的 regmap(如果支持),请使用:

struct regmap *regulator_get_regmap(struct regulator *regulator);

要获取稳压器电压选择器寄存器的硬件寄存器偏移和位掩码,请使用:

int regulator_get_hardware_vsel_register(struct regulator *regulator,
                                         unsigned *vsel_reg,
                                         unsigned *vsel_mask);

要将稳压器框架电压选择器代码(由 regulator_list_voltage 使用)转换为可以直接写入电压选择器寄存器的特定于硬件的电压选择器,请使用:

int regulator_list_hardware_vsel(struct regulator *regulator,
                                 unsigned selector);

要访问用于使能/禁用稳压器的硬件,消费者必须使用 regulator_get_exclusive(),因为它在有多个消费者的情况下无法工作。要使能/禁用稳压器,请使用:

int regulator_hardware_enable(struct regulator *regulator, bool enable);