1、簡介
??regulator 框架是 Linux 內核中用于管理電壓和電流調節器(如 LDO、DCDC 轉換器等)的一個子系統。它提供了一個抽象層,使得驅動程序和內核的其他部分可以以一致的方式與調節器進行交互,而無需了解底層硬件的細節。
主要功能包括:
- 控制電壓的啟停
- 調整輸出電壓
- 查詢當前電壓狀態
- 保護系統不受過流或過壓的影響
2、設備樹中的 regulator
??設備樹中常見的有兩種 regulator,一種是可變的 regulator,一種是固定的 regulator。
2.1 固定 regulator
??固定 regulator 在 Linux 設備樹中的 compatible 為 “regulator-fixed”。固定,顧名思義,該 regulator 不支持電壓調節,只支持 enable/disable。
??regulator-fixed 通常是由一個 GPIO 控制了一路 regulator,如下圖,GPIO3_C3 控制了 TP6327GS6 電源芯片,輸出 MINIPCIE_3V3 電壓給到 MINIPCIE 部件。
對應的設備樹節點如下:
vcc3v3_m2_pcie: vcc3v3-m2-pcie-regulator {compatible = "regulator-fixed";regulator-name = "m2_pcie_3v3";enable-active-high;regulator-min-microvolt = <3300000>;regulator-max-microvolt = <3300000>;gpios = <&gpio0 RK_PD4 GPIO_ACTIVE_HIGH>;pinctrl-0 = <&vcc3v3_m2_pcie_en>;pinctrl-names = "default";startup-delay-us = <200000>;vin-supply = <&vcc5v0_sys>;};
解析如下:
- regulator-min-microvolt:固定電壓(對于 regulator-fixed 來說,min 和 max 一定相同)
- gpios :控制 regulator 使能的 gpio 引腳(當調用 enable regulator 接口時就是使能該 gpio 引腳)
- startup-delay-us:啟動時間(微秒)
- enable-active-high:GPIO 極性為高電平有效。如果缺少此屬性,則默認為低電平有效。
- regulator-boot-on:指示該 regulator 在 bootloader/firmware 階段已經被 enable 了(注意,只能確定 bootloader 階段,無法確定系統啟動后的一個狀態)
- regulator-always-on:該 regulator 永遠不應該被禁用,系統一上電就應該使能該 regulator
- gpio-open-drain:GPIO 為開漏類型。如果缺少此屬性,則默認假設為 false
- vin-supply:該 regulator 輸入源
關于 regulator-fixed 詳細節點屬性,見
Linux/Documentation/devicetree/bindings/regulator/fixed-regulator.yaml
regulator-fixed
對應的驅動文件:drivers/regulator/fixed.c
static const struct regulator_ops fixed_voltage_ops = {
};
可以看到,fixed_voltage_ops
結構為空,不支持調節電壓。
2.2 可變 regulator
??可變 regulator,常見的,又可分為 regulator-gpio 和 普通 regualtor。
2.2.1 regulator-gpio
??通常是由 GPIO 控制的 regulator。可以通過一個或多個 GPIO 控制輸出多個不同的電壓或電流,因此除了開(enable) \ 關(disabled)兩種操作外,往往還支持電壓或電流的控制。
具體是電壓還是電流,是由設備樹節點中的 regulator-type 屬性決定的。如果沒有該屬性,默認是電壓調節
對應的 Linux 設備樹節點如下:
gpio-regulator {compatible = "regulator-gpio";regulator-name = "mmci-gpio-supply";regulator-min-microvolt = <1800000>;regulator-max-microvolt = <2600000>;regulator-boot-on;enable-gpios = <&gpio0 23 0x4>;gpios = <&gpio0 24 0x4&gpio0 25 0x4>;states = <1800000 0x3>,<2200000 0x2>,<2600000 0x1>,<2900000 0x0>;startup-delay-us = <100000>;enable-active-high;};
解析如下:
- regulator-min-microvolt:描述該電源輸出電壓的允許范圍最小值 1.8v
- regulator-max-microvolt:描述該電源輸出電壓的允許范圍最大值 2.6v
- regulator-boot-on:指示該 regulator 在 bootloader/firmware 階段已經被 enable 了(注意,只能確定 bootloader 階段,無法確定系統啟動后的一個狀態)
- enable-gpios:用來控制該 regulator 開關的 gpio 引腳
- gpios:一個或多個 GPIO 引腳數組,用于選擇 “states” 中列出的調節器電壓/電流
- states:此 regulator 提供的可用電壓/電流,并匹配相應的 GPIO 配置以實現這些電壓/電流
- gpios-states:在不支持在輸出模式下讀取 GPIO 值的操作系統上(尤其是 Linux),此數組提供從 GPIO 控制器請求 GPIO 引腳時設置的 GPIO 引腳狀態(可以理解為設置 gpios 的初始狀態)。gpios-states 屬性中的元素和 gpios 屬性中的元素一一對應
- startup-delay-us:啟動時間(微秒)
- enable-active-high:GPIO 極性為高電平有效。如果缺少此屬性,則默認為低電平有效。
關于 regulator-gpio 詳細節點屬性,見
Linux/Documentation/devicetree/bindings/regulator/gpio-regulator.yaml
??關于 states
和 gpios
屬性這里詳細解釋一下,這兩個屬性是成對出現。regulator-gpio
本質上,是通過一組 GPIO(也就是 gpios
屬性中的),來控制不同的電壓輸出。可提供的電壓輸出選項(也就是 states
屬性中的)。
gpio0_24 電平 | gpio0_25 電平 | 輸出電壓 |
---|---|---|
0 | 0 | 2.9v |
0 | 1 | 2.2v |
1 | 0 | 2.6v |
1 | 1 | 1.8v |
2.2.2 普通 regulator
vdd_cpu: regulator@1c {compatible = "tcs,tcs4525";reg = <0x1c>;fcs,suspend-voltage-selector = <1>;regulator-name = "vdd_cpu";regulator-always-on;regulator-boot-on;regulator-min-microvolt = <800000>;regulator-max-microvolt = <1150000>;regulator-ramp-delay = <2300>;vin-supply = <&vcc5v0_sys>;regulator-state-mem {regulator-off-in-suspend;};};
解析如下:
- regulator-min-microvolt:描述該電源輸出電壓的允許范圍最小值 0.8v
- regulator-max-microvolt:描述該電源輸出電壓的允許范圍最大值 1.15v
- regulator-ramp-delay:改變電壓到電源穩定后需要的時間
- regulator-boot-on:指示該 regulator 在 bootloader/firmware 階段已經被 enable 了(注意,只能確定 bootloader 階段,無法確定系統啟動后的一個狀態)
- regulator-always-on:該 regulator 永遠不應該被禁用,系統一上電就應該使能該 regulator
- vin-supply:該 regulator 輸入源
3、Linux 源碼中的 regulator 驅動
3.1 框架
3.2 machine
??machin 使用 struct regulator_init_data
,靜態的描述 regulator 在板級的硬件連接情況。這些限制通過驅動或 dts 配置,涉及到系統供電安全,因此必須小心,這些配置主要包括:
- 用于描述 regulator 在板級的級聯關系:前級 regulator(即該 regulator 的輸出是另一個 regulator 的輸入,簡稱 supply regulator)和后級 regulator(即該 regulator 的輸入是其它 regulator 的輸出,簡稱 consumer regulator)
- 利用
struct regulation_constraints
描述 regulator 的物理限制,比如:- 輸出電壓的最大值和最小值(voltage regulator)
- 輸出電流的最大值和最小值(current regulator)
- 允許的操作(修改電壓值、修改電流限制、enable、disable等)
- 輸入電壓是多少(當輸入是另一個regulator時)
- 是否不允許關閉(always_on)
- 是否啟動時就要打開(always_on)
涉及到的 Linux 源碼文件:Linux/include/linux/regulator/machine.h
/*** struct regulator_init_data - regulator platform initialisation data.** Initialisation constraints, our supply and consumers supplies.** @supply_regulator: Parent regulator. Specified using the regulator name* as it appears in the name field in sysfs, which can* be explicitly set using the constraints field 'name'.** @constraints: Constraints. These must be specified for the regulator to* be usable.* @num_consumer_supplies: Number of consumer device supplies.* @consumer_supplies: Consumer device supply configuration.** @regulator_init: Callback invoked when the regulator has been registered.* @driver_data: Data passed to regulator_init.*/
struct regulator_init_data {const char *supply_regulator; /* or NULL for system supply */struct regulation_constraints constraints;int num_consumer_supplies;struct regulator_consumer_supply *consumer_supplies;/* optional regulator machine specific init */int (*regulator_init)(void *driver_data);void *driver_data; /* core does not touch this */
};/*** struct regulation_constraints - regulator operating constraints.** This struct describes regulator and board/machine specific constraints.** @name: Descriptive name for the constraints, used for display purposes.** @min_uV: Smallest voltage consumers may set.* @max_uV: Largest voltage consumers may set.* @uV_offset: Offset applied to voltages from consumer to compensate for* voltage drops.** @min_uA: Smallest current consumers may set.* @max_uA: Largest current consumers may set.* @ilim_uA: Maximum input current.* @system_load: Load that isn't captured by any consumer requests.** @over_curr_limits: Limits for acting on over current.* @over_voltage_limits: Limits for acting on over voltage.* @under_voltage_limits: Limits for acting on under voltage.* @temp_limits: Limits for acting on over temperature.** @max_spread: Max possible spread between coupled regulators* @max_uV_step: Max possible step change in voltage* @valid_modes_mask: Mask of modes which may be configured by consumers.* @valid_ops_mask: Operations which may be performed by consumers.** @always_on: Set if the regulator should never be disabled.* @boot_on: Set if the regulator is enabled when the system is initially* started. If the regulator is not enabled by the hardware or* bootloader then it will be enabled when the constraints are* applied.* .....* .....*/
struct regulation_constraints {const char *name;/* voltage output range (inclusive) - for voltage control */int min_uV;int max_uV;int uV_offset;/* current output range (inclusive) - for current control */int min_uA;int max_uA;int ilim_uA;int system_load;/* used for coupled regulators */u32 *max_spread;/* used for changing voltage in steps */int max_uV_step;/* valid regulator operating modes for this machine */unsigned int valid_modes_mask;/* valid operations for regulator on this machine */unsigned int valid_ops_mask;/* regulator input voltage - only if supply is another regulator */int input_uV;......
}
3.3 regulator driver
??regulator driver 指的是具體的 regulator 設備的驅動,例如:
- regulator-gpio 這一類的 regulator 驅動,對應的驅動文件 Linux/drivers/regulator/gpio-regulator.c
- regulator-fixed 這一類的 regulator 驅動,對應的驅動文件 Linux/drivers/regulator/fixed.c
- tcs,tcs4525 這種具體的 regulator 驅動,對應的驅動文件 Linux/drivers/regulator/fan53555.c
??regulator drive 要做的事,是操作底層具體的 regulator 硬件寄存器,控制 regulator 輸出的電流、電壓。同時將控制電壓、電流的接口封裝,注冊、提交給 regulator framework core 層。
??在內核 Linux/kernel/drivers/regulator/dummy.c 文件中構造了一個虛擬的 regulator,給各種 regulator 驅動開發提供的參考。
// SPDX-License-Identifier: GPL-2.0-or-later
/** dummy.c** Copyright 2010 Wolfson Microelectronics PLC.** Author: Mark Brown <broonie@opensource.wolfsonmicro.com>** This is useful for systems with mixed controllable and* non-controllable regulators, as well as for allowing testing on* systems with no controllable regulators.*/#include <linux/err.h>
#include <linux/export.h>
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>#include "dummy.h"struct regulator_dev *dummy_regulator_rdev;/* * 提供 machine 層的 regulator_init_data 資源* 通常在 regulator 驅動中,regulator_init_data 資源是通過設備樹解析出來的。* 調用 of_get_regulator_init_data 設備樹接口,而不是像下面這樣直接在驅動中固定* 解析出的 machine 信息保存在 struct regulator_config 結構中* 通過調用 devm_regulator_register 系統接口,將接口注冊進 regulator framework core 中*/
static const struct regulator_init_data dummy_initdata = {.constraints = {.always_on = 1,},
};/** 封裝具體的控制 regulator 電壓、電流的函數接口(通過操作一些 regulaor 硬件寄存器)* 存放在 struct regulator_desc 結構中* 通過調用 devm_regulator_register 系統接口,將接口注冊進 regulator framework core 中*/
static const struct regulator_ops dummy_ops;static const struct regulator_desc dummy_desc = {.name = "regulator-dummy",.id = -1,.type = REGULATOR_VOLTAGE,.owner = THIS_MODULE,.ops = &dummy_ops,
};static int dummy_regulator_probe(struct platform_device *pdev)
{struct regulator_config config = { };int ret;config.dev = &pdev->dev;config.init_data = &dummy_initdata;/* * 向 regulator framework core 注冊接口* 函數入參 struct regulator_desc、struct regulator_config*/dummy_regulator_rdev = devm_regulator_register(&pdev->dev, &dummy_desc,&config);if (IS_ERR(dummy_regulator_rdev)) {ret = PTR_ERR(dummy_regulator_rdev);pr_err("Failed to register regulator: %d\n", ret);return ret;}return 0;
}static struct platform_driver dummy_regulator_driver = {.probe = dummy_regulator_probe,.driver = {.name = "reg-dummy",.probe_type = PROBE_PREFER_ASYNCHRONOUS,},
};static struct platform_device *dummy_pdev;void __init regulator_dummy_init(void)
{int ret;dummy_pdev = platform_device_alloc("reg-dummy", -1);if (!dummy_pdev) {pr_err("Failed to allocate dummy regulator device\n");return;}ret = platform_device_add(dummy_pdev);if (ret != 0) {pr_err("Failed to register dummy regulator device: %d\n", ret);platform_device_put(dummy_pdev);return;}ret = platform_driver_register(&dummy_regulator_driver);if (ret != 0) {pr_err("Failed to register dummy regulator driver: %d\n", ret);platform_device_unregister(dummy_pdev);}
}
3.4 regulator framework core
??regulator framework core 屏蔽了底層各種 regulaor 硬件的差異,對上面的 regulator consumer 提供統一的控制類接口。并提供接口給內核中其他的 consumer(使用當前 regulator 設備的驅動),并以 sysfs 的形式,向用戶空間提供接口。
??regulator framework core 主要涉及的文件是:Linux/drivers/regulator/core.c
??該文件涉及了 regulator framework core 層接口的具體實現。例如,實現 regulator driver 層注冊 regulator 的接口: devm_regulator_register
等。
3.5 regulator consumer
??regulator consumer 抽象出 regulator 設備(struct regulator),并提供 regulator 操作相關的接口。包括:
- regulator_get/regulator_put
- regulator_enable/regulator_disable
- regulator_set_voltage/regulator_get_voltage 等
??以調節 CPU 電壓為例:
vdd_cpu: regulator@1c {compatible = "tcs,tcs4525";reg = <0x1c>;fcs,suspend-voltage-selector = <1>;regulator-name = "vdd_cpu";regulator-always-on;regulator-boot-on;regulator-min-microvolt = <800000>;regulator-max-microvolt = <1150000>;regulator-ramp-delay = <2300>;vin-supply = <&vcc5v0_sys>;regulator-state-mem {regulator-off-in-suspend;};};&cpu0 {cpu-supply = <&vdd_cpu>;
};&cpu1 {cpu-supply = <&vdd_cpu>;
};&cpu2 {cpu-supply = <&vdd_cpu>;
};&cpu3 {cpu-supply = <&vdd_cpu>;
};
consumer 代碼調用流程:
dt_cpufreq_early_init--> dev_pm_opp_set_regulators--> regulator_get_optional
通過解析 CPU 設備樹節點 cpu-supply
屬性,調用 regulator_get_optional
找到 CPU 對應的 regulator。
struct regulator * regulator_get_optional(struct device * dev, const char * id);
Parameters
- struct device * dev
- device for regulator “consumer”
- const char * id
- Supply name or regulator ID
獲取到對應的 regulator 結構后,會調用 regulator_set_voltage
設置具體的 CPU 電壓
static int _set_opp_voltage(struct device *dev, struct regulator *reg,struct dev_pm_opp_supply *supply)
{int ret;/* Regulator not available for device */if (IS_ERR(reg)) {dev_dbg(dev, "%s: regulator not available: %ld\n", __func__,PTR_ERR(reg));return 0;}dev_dbg(dev, "%s: voltages (mV): %lu %lu %lu\n", __func__,supply->u_volt_min, supply->u_volt, supply->u_volt_max);ret = regulator_set_voltage_triplet(reg, supply->u_volt_min,supply->u_volt, supply->u_volt_max);if (ret)dev_err(dev, "%s: failed to set voltage (%lu %lu %lu mV): %d\n",__func__, supply->u_volt_min, supply->u_volt,supply->u_volt_max, ret);return ret;
}
4、關于電壓操作的兩種方式
Linux kernel 抽象了兩種電壓操作的方法:
- 直接操作電壓
- 以 selector 的形式
4.1 直接操作電壓
??對應 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 (*get_voltage) (struct regulator_dev *);
- set_voltage, 用于將電壓設置為 min_uV 和 max_uV 范圍內、和 min_uV 最接近的電壓。該接口可以返回一個 selector 參數,用于告知調用者,實際的電壓值對應的索引
- get_voltage,用于返回當前的真實電壓值
- list_voltage,以 selector 為參數,獲取 selector 對應的真實電壓值
??這里的 selector 可以理解為,電壓值對應的索引。這個值的類型、計算方法,每個 regulator 驅動都可能不同(也可以里理解為,只有 regulator 驅動本人才知道 selector 是啥)。以 regulator-gpio
驅動為例:
static int gpio_regulator_set_voltage(struct regulator_dev *dev,int min_uV, int max_uV,unsigned *selector)
{struct gpio_regulator_data *data = rdev_get_drvdata(dev);int ptr, target = 0, state, best_val = INT_MAX;for (ptr = 0; ptr < data->nr_states; ptr++)if (data->states[ptr].value < best_val &&data->states[ptr].value >= min_uV &&data->states[ptr].value <= max_uV) {target = data->states[ptr].gpios;best_val = data->states[ptr].value;/* 從這里也可以看到,返回的 selector 就是將要設置的電壓對應的設備樹節點 states 中的電壓編號 */if (selector)*selector = ptr;}if (best_val == INT_MAX)return -EINVAL;for (ptr = 0; ptr < data->nr_gpios; ptr++) {state = (target & (1 << ptr)) >> ptr;gpiod_set_value_cansleep(data->gpiods[ptr], state);}data->state = target;return 0;
}
4.2 以 selector 的形式
??regulator driver 以 selector 的形式,反映電壓值。selector 是一個從 0 開始的整數,driver 提供如下的接口:
int (*map_voltage)(struct regulator_dev *, int min_uV, int max_uV);
int (*set_voltage_sel) (struct regulator_dev *, unsigned selector);
int (*get_voltage_sel) (struct regulator_dev *);
- map_voltage,是和 list_voltage 相對的接口,用于將電壓范圍 map 成一個 selector(這個電壓范圍對應的一個索引)
- set_voltage_sel,以 selector 的形式,設置電壓,函數入參為 selector
- get_voltage_sel,以 selector 的形式,讀取電壓,返回值為 selector
以瑞芯微 rk809 regulator 驅動為例:
/*** regulator_map_voltage_linear_range - map_voltage() for multiple linear ranges** @rdev: Regulator to operate on* @min_uV: Lower bound for voltage* @max_uV: Upper bound for voltage** Drivers providing linear_ranges in their descriptor can use this as* their map_voltage() callback.*/
int regulator_map_voltage_linear_range(struct regulator_dev *rdev,int min_uV, int max_uV)
{const struct linear_range *range;int ret = -EINVAL;unsigned int sel;bool found;int voltage, i;if (!rdev->desc->n_linear_ranges) {BUG_ON(!rdev->desc->n_linear_ranges);return -EINVAL;}for (i = 0; i < rdev->desc->n_linear_ranges; i++) {range = &rdev->desc->linear_ranges[i];ret = linear_range_get_selector_high(range, min_uV, &sel,&found);if (ret)continue;ret = sel;/** Map back into a voltage to verify we're still in bounds.* If we are not, then continue checking rest of the ranges.*/voltage = rdev->desc->ops->list_voltage(rdev, sel);if (voltage >= min_uV && voltage <= max_uV)break;}if (i == rdev->desc->n_linear_ranges)return -EINVAL;return ret;
}
??regulator 的輸出電壓通過內部的寄存器值(selector 或 index)控制的,寄存器值與輸出電壓之間有一個固定的映射關系。這個映射關系可以用以下公式表示:
V o u t = V m i n + s e l e c t o r × u V s t e p V_{out}=V_{min}+selector×uV_{step} Vout?=Vmin?+selector×uVstep?
其中:
- V_ o u t {out} out 是當前輸出電壓
- V_ m i n {min} min 是調節器支持的最低輸出電壓
- uV_ s t e p {step} step 是調節電的壓步長
- selector 是一個整數(從 0 開始),對應電壓級別。
硬件只支持離散的 selector 值,因此電壓只能按照 uV_step 的倍數變化。
例如:
/*** struct linear_range - table of selector - value pairs** Define a lookup-table for range of values. Intended to help when looking* for a register value matching certaing physical measure (like voltage).* Usable when increment of one in register always results a constant increment* of the physical measure (like voltage).** @min: Lowest value in range* @min_sel: Lowest selector for range* @max_sel: Highest selector for range* @step: Value step size*/
struct linear_range {unsigned int min;unsigned int min_sel;unsigned int max_sel;unsigned int step;
};static const struct regulator_linear_range my_ranges[] = {{.min_uV = 800000,.min_sel = 0,.max_sel = 15,.uV_step = 25000,},
};
在這種定義下:
- selector = 0 → 800,000 μV
- selector = 1 → 825,000 μV
- …
- selector = 15 → 1,175,000 μV
4.3 總結
??以上兩種方式,regulator driver 可以根據實際情況,選擇一種實現方式。