电源管理入门-11Regulator驱动

张开发
2026/4/4 0:13:03 15 分钟阅读
电源管理入门-11Regulator驱动
上一篇讲了OPP里面包含了电压和频率那么电压具体控制驱动就是regulator驱动频率就是clock驱动我们分两节介绍。1. Regulator驱动是什么Regulator是Linux系统中电源管理的基础设施之一用于稳压电源的管理是各种驱动子系统中设置 电压的标准接口。前面介绍的CPUFreq驱动就经常使用它来设定电压。分为voltage regulator(电压调节器)和current(电流调节器)。一般电源管理芯片(Power Management IC)中会包含一个甚至多个regulator。而Regulator则可以管理系统中的供电单元即稳压器Low Dropout RegulatorLDO即低压差线性 稳压器并提供获取和设置这些供电单元电压的接口。一般在ARM电路板上各个稳压器和设备会形 成一个Regulator树形结构**Regulator的作用是什么通常的作用是给电子设备供电。大多数regulator可以启用(enable)和禁用(disable)其输出同时也可以控制其输出电压(voltage)和电流(current)。从上图可以看出input power会经过 regulator 转化为output powerregulator可以做如下的约束:Voltage control: 限制输出的电压Current limiting: 限制最大输出电流Power switch: 可以控制电压enable/disablePower Domain电源域由稳压器、开关或其他电源域的输出电源提供其输入电源的电子电路。电源Regulator可能位于一个或多个开关后面例如Regulator --Switch-1 --Switch-2 --[Consumer A]|||-[Consumer B],[Consumer C]|-[Consumer D],[Consumer E]这是一个稳压器和三个电源域Domain1: Switch-1, Consumers DE. Domain2: Switch-2, Consumers BC. Domain3: Consumer A.Regulator电压设计时的约束稳压器级别这由稳压器硬件操作参数定义并在稳压器数据表中指定例如 电压输出范围为 800mV - 3500mV 稳压器电流输出限制为 20mA 5V但为 10mA 10V功率域级别这是由内核级板初始化代码在软件中定义的。它用于将功率域限制在特定的功率范围内例如Domain-2 电压为 1400mV - 1600mVConsumer级别这是由Consumer驱动程序动态设置电压或电流限制级别定义的。例如 消费类背光驱动器要求将电流从 5mA 增加到 10mA以增加 LCD 亮度。2. Regulator框架介绍Linux regulator framework的主要目的提供标准的内核接口控制系统的voltage/current regulators并提供相应的开关、大小设置的机制。在系统运行的过程中根据具体的需要动态改变regulators的输出从而达到省电的目的。在系统中如果配错regulator是比较危险的可能会造成硬件器件的损坏。因此需要在regulator framework中对电流或者电压的大小做限定并且不能被ragulator的consumer或者provider更改。2.1 regulator consumerregulator consumer抽象出regulator设备struct regulator并提供regulator操作相关的接口。包括regulator_get/regulator_put/regulator_enable/regulator_disable/ regulator_set_voltage/regulator_get_voltage等。2.2 regulator coreregulator core负责上述regulator driver/consumer/machine逻辑的具体实现对底层的硬件进行封装并提供接口给内核中其他的consumer使用当前regulator设备的驱动提供操作接口并以sysfs的形式向用户空间提供接口。2.3 regulator driverregulator driver指的是regulator设备的驱动主要包含如下结构1使用struct regulator_desc描述regulator的静态信息包括名字、supply regulator的名字、中断号、操作函数集struct regulator_ops、使用regmap时相应的寄存器即bitmap等。2使用struct regulator_config描述regulator的动态信息所谓的动态信息体现在struct regulator_config变量都是局部变量因此不会永久保存包括struct regulator_init_data指针、设备指针、enable gpio等。3提供regulator的注册接口regulator_register/devm_regulator_register该接口接受描述该regulator的两个变量的指针struct regulator_desc和struct regulator_config并分配一个新的数据结构struct regulator_dev从设备的角度描述regulator并把静态指针struct regulator_desc和动态指针struct regulator_config提供的信息保存在其中。4regulator driver以struct regulator_dev代表设备指针为对象对regulator进行后续的操作。3. DTS配置文件及初始化例如arch/arm/boot/dts/100ask_imx6ull_qemu.dts中regulators{compatiblesimple-bus;#address-cells 1;#size-cells 0;reg_can_3v3: regulator0{compatibleregulator-fixed;reg0;regulator-namecan-3v3;regulator-min-microvolt3300000;regulator-max-microvolt3300000;/*gpiosgpio_spi3GPIO_ACTIVE_LOW;*/};subsys_initcall(regulator_fixed_voltage_init); //系统启动时候执行–》platform_driver_register(regulator_fixed_voltage_driver);static struct platform_driver regulator_fixed_voltage_driver{.probereg_fixed_voltage_probe, .driver{.namereg-fixed-voltage, .of_match_tableof_match_ptr(fixed_of_match), .pmreg_fixed_voltage_pm_ops,},};reg_fixed_voltage_probe --》devm_regulator_register(pdev-dev, drvdata-desc, cfg); --》rdev regulator_register(regulator_desc, config);regulator_ops指针ops是对这个稳压器硬件操作的封装其中包含获取、设置电压等的成员函数//稳压器硬件操作的封装其中包含获取、设置电压等 struct regulator_ops{/* enumerate supported voltages */ int(*list_voltage)(struct regulator_dev *, unsigned selector);/* get/set regulator voltage */ int(*set_voltage)(struct regulator_dev *, int min_uV, int max_uV, unsigned *selector);int(*map_voltage)(struct regulator_dev *, int min_uV, int max_uV);int(*set_voltage_sel)(struct regulator_dev *, unsigned selector);int(*get_voltage)(struct regulator_dev *);int(*get_voltage_sel)(struct regulator_dev *);/* get/set regulator current */ int(*set_current_limit)(struct regulator_dev *, int min_uA, int max_uA);int(*get_current_limit)(struct regulator_dev *);int(*set_input_current_limit)(struct regulator_dev *, int lim_uA);int(*set_over_current_protection)(struct regulator_dev *);int(*set_active_discharge)(struct regulator_dev *, boolenable);/* enable/disable regulator */ int(*enable)(struct regulator_dev *);int(*disable)(struct regulator_dev *);int(*is_enabled)(struct regulator_dev *);/* get/set regulator operating mode(definedinconsumer.h)*/ int(*set_mode)(struct regulator_dev *, unsigned int mode);unsigned int(*get_mode)(struct regulator_dev *);/* Time taken toenableorsetvoltage on the regulator */ int(*enable_time)(struct regulator_dev *);int(*set_ramp_delay)(struct regulator_dev *, int ramp_delay);int(*set_voltage_time)(struct regulator_dev *, int old_uV, int new_uV);int(*set_voltage_time_sel)(struct regulator_dev *, unsigned int old_selector, unsigned int new_selector);int(*set_soft_start)(struct regulator_dev *);/* report regulator status...mostother accessors report * control inputs, this reports results of combining inputs * from Linux(and other sources)with the actual load. * returns REGULATOR_STATUS_* or negative errno. */ int(*get_status)(struct regulator_dev *);/* getmostefficient regulator operating modeforload */ unsigned int(*get_optimum_mode)(struct regulator_dev *, int input_uV, int output_uV, int load_uA);/*setthe load on the regulator */ int(*set_load)(struct regulator_dev *, int load_uA);/* control and report on bypass mode */ int(*set_bypass)(struct regulator_dev *dev, boolenable);int(*get_bypass)(struct regulator_dev *dev, bool *enable);/* the operations below areforconfiguration of regulator state when * its parent PMIC enters a global STANDBY/HIBERNATE state */ /*setregulatorsuspendvoltage */ int(*set_suspend_voltage)(struct regulator_dev *, int uV);/* enable/disable regulatorinsuspendstate */ int(*set_suspend_enable)(struct regulator_dev *);int(*set_suspend_disable)(struct regulator_dev *);/*setregulatorsuspendoperating mode(definedinconsumer.h)*/ int(*set_suspend_mode)(struct regulator_dev *, unsigned int mode);int(*set_pull_down)(struct regulator_dev *);};4. 运行时调用调压前要先获取regulator handle然后利用regulator_set_voltage进行调压5. Consumer API5.1 Consumer Regulator Access (static dynamic drivers)消费者驱动程序可以通过调用regulator_get访问其供应调节器regulator regulator_get(dev, Vcc);消费者传入其结构设备指针和电源 ID。然后内核通过查询特定于机器的查找表找到正确的调节器。如果查找成功则此调用将返回一个指向提供此使用者的结构调节器的指针。要释放调节器消费者驱动程序应调用:regulator_put(regulator);消费者可以由多个调节器供电例如 具有模拟和数字电源的编解码器消费者:digitalregulator_get(dev,Vcc);/* digital core */ analogregulator_get(dev,Avdd);/* analog */调节器访问函数regulator_get() 和regulator_put() 通常会分别在您的设备驱动程序probe() 和remove() 中调用。5.2 Regulator Output Enable Disable (static dynamic drivers)消费者可以通过调用regulator_enable启用调节器int regulator_enable(regulator);在调用regulator_enabled() 之前电源可能已经启用。如果消费者共享调节器或调节器先前已由引导加载程序或内核板初始化代码启用则可能会发生这种情况。消费者可以通过调用regulator_is_enabled判断是否启用了调节器int regulator_is_enabled(regulator);当调节器启用时这将返回大于零。消费者可以在不再需要时通过调用禁用其供应int regulator_disable(regulator);如果它与其他消费者共享这可能不会禁用供应。仅当启用的参考计数为零时才会禁用调节器。最后在紧急情况下可以强制禁用调节器:int regulator_force_disable(regulator);这将立即强制关闭稳压器输出。所有消费者都将断电。5.3 Regulator Voltage Control Status (dynamic drivers)一些消费类驱动器需要能够动态改变其电源电压以匹配系统工作点。例如 CPUfreq 驱动程序可以随频率调整电压以节省电量SD 驱动程序可能需要选择正确的卡电压等。消费者可以通过调用来控制他们的电源电压:int regulator_set_voltage(regulator, min_uV, max_uV);其中 min_uV 和 max_uV 是以微伏为单位的最小和最大可接受电压。这可以在调节器启用或禁用时调用。如果在已启用regulator时调用则电压会立即更改否则电压配置会更改并且在下一次启用稳压器时会物理设置电压。调节器配置的电压输出可以通过调用找到int regulator_get_voltage(regulator);无论调节器是启用还是禁用get_voltage() 都将返回配置的输出电压并且不应用于确定调节器输出状态。然而这可以与 is_enabled() 结合使用来确定稳压器物理输出电压。5.4 Regulator Current Limit Control Status (dynamic drivers)一些消费类驱动程序需要能够动态更改其电源电流限制以匹配系统工作点。例如 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.5 Regulator Operating Mode Control Status (dynamic drivers)一些消费者可以通过改变其电源调节器的工作模式来进一步节省系统功率以便在消费者工作状态发生变化时提高效率。例如 消费者驱动程序空闲随后消耗较少的电流.调节器操作模式可以间接或直接改变。间接操作模式控制 消费者驱动程序可以通过以下调用请求更改其电源调节器操作模式int regulator_set_load(struct regulator *regulator, int load_uA);这将导致core重新计算调节器上的总负载基于其所有消费者并更改操作模式如果必要和允许以最佳匹配当前操作负载。load_uA 值可以从消费者的数据表中确定。例如 大多数数据表都有表格显示在某些情况下消耗的最大电流。大多数消费者将使用间接操作模式控制因为他们不了解调节器或调节器是否与其他消费者共享直接操作模式控制 定制或紧密耦合的驱动器可能希望根据其工作点直接控制调节器的工作模式, 这可以通过调用int regulator_set_mode(struct regulator *regulator, unsigned int mode); unsigned int regulator_get_mode(struct regulator *regulator); 直接模式将仅由了解有关调节器且不与其他消费者共享调节器的消费者使用5.6 Regulator Events监管机构可以将外部事件通知消费者, 在监管机构压力或故障条件下消费者可能会收到事件。消费者调用以下接口注册感兴趣的事件int regulator_register_notifier(struct regulator *regulator, struct notifier_block *nb); 消费者调用以下接口反注册感兴趣的事件int regulator_unregister_notifier(struct regulator *regulator, struct notifier_block *nb); 监管机构使用内核通知程序框架向感兴趣的消费者发送事件。5.7 Regulator Direct Register Access某些类型的电源管理硬件或固件被设计为需要对调节器进行低级硬件访问而无需内核参与,此类设备的示例有带有压控振荡器和控制逻辑的时钟源可通过 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); 要将调节器框架电压选择器代码由调节器列表电压使用转换为可直接写入电压选择器寄存器的特定于硬件的电压选择器请使用int regulator_list_hardware_vsel(struct regulator *regulator, unsigned selector);6. Driver Interface驱动程序可以通过调用以下接口注册Regulatorstruct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, const struct regulator_config *config); 这会将regulator的能力和操作注册到regulator核心。注销接口如下void regulator_unregister(struct regulator_dev *rdev);调节器可以通过调用以下方式向消费者驱动程序发送事件例如过热、欠压等int regulator_notifier_call_chain(struct regulator_dev *rdev, unsigned long event, void *data)最后来个大图参考https://zhuanlan.zhihu.com/p/565532795https://carlyleliu.github.io/2020/Linux%E9%A9%B1%E5%8A%A8%E4%B9%8BRegulator%E5%AD%90%E7%B3%BB%E7%BB%9F/后记电源管理的很多机制都是这种三层consumer关注于提供使用接口只通过名字就可以获取dev结构体进行控制。provider专注于驱动的具体实现就是怎么去操作寄存器需要开发者自己编写。然后core层就是一层抽象由linux kernel去编写后就不用动了大家也不用了解其实现相当于一个HAL层这个HAL层规定好要实现什么结构体定义或者函数ops定义driver去填写就可以。“啥都懂一点啥都不精通干啥都能干干啥啥不是专业入门劝退堪称程序员杂家”。欢迎各位自己有博客公众号的留言申请转载多谢后续会继续更新纯干货分析欢迎分享给朋友欢迎点赞、收藏、在看、划线和评论交流以公众号“那路谈OS与SoC嵌入式软件”欢迎关注个人文章汇总https://thatway1989.github.io

更多文章