1. 前言
本文从regulator driver的角度,描述怎样基于regulator framework编写regulator驱动。同时,以此为契机,学习、理解regulator有关的物理特性,以便能够更好的使用它们。
2. regulator driver的实现步骤
2.1 确定系统中regulator有关的硬件组成
提起硬件,最好能有个例子,好在有device tree,一个活生生的硬件拓扑结构。这里以NVIDIA Tegra Dalmore A04开发板为例(regulator有关的device tree位于“arch\arm\boot\dts\tegra114-dalmore.dts”):
这里的regulator结构是相当复杂的,其中彩色框代表最终的regulator抽象,它的前一级表示regulator的载体(可以是PMIC、CPU、等等)。下面将会详细说明:
a)CPU通过I2C controller,连接一个降压控制器(TI tps51632),该控制器输出名称为“vdd-cpu”的电压,就称作vdd-cpu regulator吧(因此,在kernel中,regulator是一个虚拟设备)。
b)CPU通过I2C controller,连接一个前端电源管理芯片(TI tps65090),该芯片除了具备充电管理功能外,内置了多个regulator,例如dcdc1、dcdc2等等。
c)CPU通过I2C controller,连接另一个电源管理芯片(TI tps65913),该芯片具有两个功能:GPIO输出和PMIC。PMIC内置了多个regulator,如vddio-ddr、vdd-core等等。
d)CPU内部也集成了一些regulator,如vdd_ac_bat等等。
注1:单纯从硬件的角度看,是不存在图中"regulators“、PMIC等实体的,它们的出现,已经包含了软件设计的思路。之所以画在这里,是方便后面的描述。
2.2 使用DTS,将硬件拓扑呈现出来
我们都知道,DTS的功能是描述设备的拓扑结构,并在系统初始化的时候,为被描述的设备创建并注册对应的platform device,最终和相应的platform driver相遇,执行其probe接口,实现设备的枚举功能。但是,在这些基本原则之外,还需要一些更深的思考:
DTS节点(node)怎么和设备对应?是以设备的“物理界限”为单位,还是以设备的“功能”为单位?
是不是所有的“设备”都应该在kernel中创建一个platform device?如果不是,创建的依据是什么?
这些思考在本文的例子(NVIDIA Tegra Dalmore A04的regulator)中体现尤为突出,它的本质是软件设计中的模块划分,从而决定了regulator在DTS中的呈现方式和层次。
1)tps51632
tps51632是一个简单的器件,位于i2c总线下面,包含一个regulator器件,因此其DTS比较简单,如下:
1: /* arch\arm\boot\dts\tegra114-dalmore.dts */
2: i2c@7000d000 {
3: status = "okay";
4: clock-frequency = <400000>;
5:
6: tps51632@43 {
7: compatible = "ti,tps51632";
8: reg = <0x43>;
9: regulator-name = "vdd-cpu";
10: regulator-min-microvolt = <500000>;
11: regulator-max-microvolt = <1520000>;
12: regulator-boot-on;
13: regulator-always-on;
14: };
15: ...
16: }
i2c控制器的node为“i2c@7000d000”,tps51632是其下的一个子node,名称为“tps51632@43”,compatible为“ti,tps51632”。tps51632下面以“regulator-”为前缀的字段,是regulator特有的字段,后面会统一介绍。
注2:为什么“i2c@7000d000”中没有compatible字段?其实是有的,可参考“arch\arm\boot\dts\tegra114.dtsi”,DTC在编译DTS时,会将这两个文件中的node合并。
注3:kernel在初始化时,只会为二级node(即“/”下面的节点,本文的例子是“i2c@7000d000”)创建platform设备,至于三级node(这里的“tps51632@43”),则由其bus(i2c)创建。后面我们会遇到其它的情况,到时再介绍。
2)tps65090
tps65090相对比较复杂,它位于相同的i2c总线下面,但包含两个相对复杂的功能实体,charger和PMIC,我们看看其DTS怎么写的:
1: i2c@7000d000 {
2: status = "okay";
3: ...
4:
5: tps65090@48 {
6: compatible = "ti,tps65090";
7: reg = <0x48>;
8: ...
9:
10: charger: charger {
11: compatible = "ti,tps65090-charger";
12: ti,enable-low-current-chrg;
13: };
14:
15: regulators {
16: tps65090_dcdc1_reg: dcdc1 {
17: regulator-name = "vdd-sys-5v0";
18: regulator-always-on;
19: regulator-boot-on;
20: };
21:
22: tps65090_dcdc2_reg: dcdc2 {
23: regulator-name = "vdd-sys-3v3";
24: regulator-always-on;
25: regulator-boot-on;
26: };
27: ...
28: }
29: }
30: }
和tps51632类似,但它下面又包含了两个子node:charger和regulators。其中charger竟然还有compatible字段。
回忆一下上面“注3”,kernel只会为"i2c@7000d000”创建platform device,“tps65090@48”则由i2c core创建,那么它下面的子node呢?一定是tps65090 driver处理了,感兴趣的读者可以阅读“drivers/mfd/tps65090.c”、“drivers/power/tps65090-charger.c”和“drivers/regulator/tps65090-regulator.c”,这里面还涉及了MFD(multi-function device,多功能设备),很有意思。
回到本文的主题上,虽然这里的regulators没有compatible字段,也会创建相应的platform device(具体可参考“drivers/mfd/tps65090.c”),这从侧面回答了上面的一个思考:从物理范畴,tps65090是一个独立的设备,但它内部有两个功能模块,因此会存在两个platform device。
再来看regulators中子node----regulator,由于数量比较多,就没必要创建platform device了。同样,“regulator-”为前缀的字段,是regulator特有的字段,后面统一介绍。
3)tps65913,和tps65090类似,不再介绍。
4)CPU中的regulator
这一类regulator比较特殊,直接集成在CPU内部,DTS如下:
1: regulators {
2: compatible = "simple-bus";
3: #address-cells = <1>;
4: #size-cells = <0>;
5:
6: vdd_ac_bat_reg: regulator@0 {
7: compatible = "regulator-fixed";
8: reg = <0>;
9: regulator-name = "vdd_ac_bat";
10: regulator-min-microvolt = <5000000>;
11: regulator-max-microvolt = <5000000>;
12: regulator-always-on;
13: };
14:
15: dvdd_ts_reg: regulator@1 {
16: compatible = "regulator-fixed";
17: reg = <1>;
18: regulator-name = "dvdd_ts";
19: regulator-min-microvolt = <1800000>;
20: regulator-max-microvolt = <1800000>;
21: enable-active-high;
22: gpio = <&gpio TEGRA_GPIO(H, 5) GPIO_ACTIVE_HIGH>;
23: };
24: ...
25: };
在回到刚才的话题上,kernel只为二级node创建platform device(这里的“regulators”),那三级node(一个个的regulator)呢?没有相对标准的bus帮它们创建怎么办?借助“simple-bus”,具体可以参考of_platform_bus_create(“Device Tree(三):代码分析”)。
另外,这里的例子比较简单,都是fixed regulator,regulator framework core可以帮忙实现fixed类型的regulator的驱动,后面会说明。
2.3 编写与DTS节点对应的driver
这些driver的存在形式是多种多样的,但所做的工作基本类似:
1)初始化regulator的宿主(如上面的tps5163、PMIC、等等),最终的目的是,通过宿主提供的接口,修改regulator的输出。
2)初始化用于描述regulator的静态信息(struct regulator_desc)和动态信息(struct regulator_config),并以这二者为参数,调用regulator_register接口,将regulator注册到kernel中。
3)静态信息中包含regulator的操作函数集(struct regulator_ops),后续regulator的控制,将会由regulator framework core直接调用这些回调函数完成。
4)后面的事情,例如sysfs attribute创建等,就交给regulator framework core了。
3. DTS相关的实现逻辑
3.1 DTS的内容
回忆一下“Linux Regulator Framework(1)_概述”中介绍的machine的主要功能:使用软件语言(struct regulator_init_data),静态的描述regulator在板级的物理现状。对regulator driver而言,DTS主要用于配置regulator的init data。先看一下struct regulator_init_data:
/**
* 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 */
};
supply_regulator:父调节器的名称,通常在sysfs的名称字段中表示。这个字段指定调节器的父调节器,可以为NULL,表示没有父调节器(通常是系统供电)。
constraints:包含初始化调节器时的一些限制和规格的结构体。这些限制和规格包括电压范围、电流限制等,以确保调节器的安全和正确操作。
num_consumer_supplies:消费者设备供应的数量。这表示与该调节器相关的消费者设备需要的电源供应数量。
consumer_supplies:与消费者设备相关的电源供应配置。这是一个指向 struct regulator_consumer_supply 数组的指针,每个元素描述一个供应配置,包括供应名称和相关约束。
regulator_init:一个可选的回调函数,当调节器被成功注册时会被调用。这允许机器特定的初始化操作,如设置特定寄存器值。
driver_data:传递给 regulator_init 回调函数的数据指针,用于执行初始化操作。
总之,struct regulator_init_data 结构体允许开发者为调节器提供必要的初始化信息和回调函数,以便正确配置和初始化电压和电流调节器。这是调节器子系统中的重要数据结构,用于确保系统中的电源供应的正确和可控性。
看来DTS的内容都在struct regulation_constraints中,该结构保存了该regulator所有的物理限制,如下:
/**
* struct regulation_constraints - 调节器的工作限制条件
*
* 该结构描述了调节器和特定板子/机器的约束条件。
*
* @name: 约束条件的描述性名称,用于显示目的。
*
* @min_uV: 消费者可以设置的最小电压。
* @max_uV: 消费者可以设置的最大电压。
* @uV_offset: 用于从消费者到调节器的电压偏移补偿,以补偿电压下降。
*
* @min_uA: 消费者可以设置的最小电流。
* @max_uA: 消费者可以设置的最大电流。
* @ilim_uA: 最大输入电流。
* @system_load: 未被任何消费者请求捕获的负载。
*
* @over_curr_limits: 用于处理过流的限制。
* @over_voltage_limits: 用于处理过压的限制。
* @under_voltage_limits: 用于处理欠压的限制。
* @temp_limits: 用于处理过温度的限制。
*
* @max_spread: 耦合的调节器之间可能的最大差距。
* @max_uV_step: 电压更改的最大步进量。
* @valid_modes_mask: 可由消费者配置的有效模式的掩码。
* @valid_ops_mask: 可由消费者执行的操作的掩码。
*
* @always_on: 如果调节器永远不应该被禁用,则设置为1。
* @boot_on: 如果调节器在系统初始启动时启用,则设置为1。
* 如果调节器不由硬件或引导加载程序启用,
* 那么它将在应用约束时启用。
* @apply_uV: 在初始化时应用电压约束。
* @ramp_disable: 在初始化或设置电压时禁用电压斜升延迟。
* @soft_start: 启用软启动,以使电压缓慢上升。
* @pull_down: 当调节器关闭时启用下拉。
* @over_current_protection: 在过流事件时自动禁用。
*
* @over_current_detection: 配置过流限制。
* @over_voltage_detection: 配置过压限制。
* @under_voltage_detection: 配置欠压限制。
* @over_temp_detection: 配置过温度限制。
*
* @input_uV: 当由另一个调节器供电时,调节器的输入电压。
*
* @state_disk: 磁盘模式下系统挂起时的调节器状态。
* @state_mem: 内存模式下系统挂起时的调节器状态。
* @state_standby: 待机模式下系统挂起时的调节器状态。
* @initial_state: 默认设置的挂起状态。
* @initial_mode: 启动时设置的模式。
* @ramp_delay: 电压更改后的稳定时间(单位:微伏/微秒)。
* @settling_time: 电压更改后的稳定时间,当电压更改不是线性时(单位:微秒)。
* @settling_time_up: 电压升高后的稳定时间,当电压更改不是线性时(单位:微秒)。
* @settling_time_down: 电压降低后的稳定时间,当电压更改不是线性时(单位:微秒)。
* @active_discharge: 启用/禁用主动放电。使用 regulator_active_discharge 枚举值进行初始化。
* @enable_time: 调节器开启时间(单位:微秒)。
*/
struct regulation_constraints {
const char *name;
/* 电压输出范围(包括)- 用于电压控制 */
int min_uV;
int max_uV;
int uV_offset;
/* 电流输出范围(包括)- 用于电流控制 */
int min_uA;
int max_uA;
int ilim_uA;
int system_load;
/* 用于耦合的调节器 */
u32 *max_spread;
/* 用于更改电压的步进 */
int max_uV_step;
/* 该机器的有效调节器工作模式 */
unsigned int valid_modes_mask;
/* 该机器上调节器操作的有效操作 */
unsigned int valid_ops_mask;
/* 调节器输入电压 - 仅当供电是另一个调节器时才使用 */
int input_uV;
/* 全局PMIC STANDBY/HIBERNATE挂起状态的调节器状态 */
struct regulator_state state_disk;
struct regulator_state state_mem;
struct regulator_state state_standby;
struct notification_limit over_curr_limits;
struct notification_limit over_voltage_limits;
struct notification_limit under_voltage_limits;
struct notification_limit temp_limits;
suspend_state_t initial_state; /* 初始化时设置的挂起状态 */
/* 启动时设置的模式 */
unsigned int initial_mode;
unsigned int ramp_delay;
unsigned int settling_time;
unsigned int settling_time_up;
unsigned int settling_time_down;
unsigned int enable_time;
unsigned int active_discharge;
/* 约束条件标志 */
unsigned always_on:1; /* 系统运行时永远不关闭的调节器 */
unsigned boot_on:1; /* 引导加载程序/固件启用的调节器 */
unsigned apply_uV:1; /* 如果最小 == 最大,则应用uV约束 */
unsigned ramp_disable:1; /* 禁用电压斜升延迟 */
unsigned soft_start:1; /* 缓慢提升电压 */
unsigned pull_down:1; /* 关闭调节器时拉低电压 */
unsigned over_current_protection:1; /* 过流时自动禁用 */
unsigned over_current_detection:1; /* 通知过流 */
unsigned over_voltage_detection:1; /* 通知过压 */
unsigned under_voltage_detection:1; /* 通知欠压 */
unsigned over_temp_detection:1; /* 通知过温度 */
};
结合struct regulation_constraints结构,我们解释一下2.2小节中tps51632的DTS:
1: tps51632@43 {
2: compatible = "ti,tps51632";
3: reg = <0x43>;
4: regulator-name = "vdd-cpu";
5: regulator-min-microvolt = <500000>;
6: regulator-max-microvolt = <1520000>;
7: regulator-boot-on;
8: regulator-always-on;
9: };
regulator-name,对应struct regulation_constraints中name;
regulator-min-microvolt,对应struct regulation_constraints中的min_uV;
regulator-max-microvolt,对应struct regulation_constraints中的max_uV;
regulator-boot-on,对应struct regulation_constraints中的boot_on;
regulator-always-on,对应struct regulation_constraints中的always_on。
其它的字段,可以根据实际情况,自行添加,具体可参考“Documentation/devicetree/bindings/regulator/regulator.txt”中的描述。
3.2 DTS的解析
regulator的DTS信息,可以通过两种方法解析:
1)在regulator注册前,调用of_get_regulator_init_data接口自行解析,该接口的实现如下:
/**
* of_get_regulator_init_data - 从设备树节点中提取 regulator_init_data 结构信息
* @dev: 请求 regulator_init_data 的设备
* @node: regulator 设备节点
* @desc: regulator 描述信息
*
* 从设备树节点中提取数据以填充 regulator_init_data 结构,
* 返回一个指向已填充结构的指针,如果内存分配失败则返回 NULL。
*/
struct regulator_init_data *of_get_regulator_init_data(struct device *dev,
struct device_node *node,
const struct regulator_desc *desc)
{
struct regulator_init_data *init_data;
// 检查是否存在有效的节点
if (!node)
return NULL;
// 为 init_data 分配内存,并初始化为零
init_data = devm_kzalloc(dev, sizeof(*init_data), GFP_KERNEL);
if (!init_data)
return NULL; /* 内存分配失败? */
// 从设备树中获取调节器的约束条件并填充到 init_data 中
if (of_get_regulation_constraints(dev, node, &init_data, desc))
return NULL;
// 返回填充后的 init_data 结构指针
return init_data;
}
EXPORT_SYMBOL_GPL(of_get_regulator_init_data);
总之,这段代码的作用是根据设备树中的节点和调节器描述信息,创建并初始化一个 regulator_init_data 结构,以便配置和初始化电压和电流调节器。这对于驱动程序和硬件初始化过程中的电源管理非常重要。
2)在regulator注册时,由regulator_register调用regulator_of_get_init_data帮忙解析,该接口的实现如下:
/**
* regulator_of_get_init_data - 从设备树中获取调节器初始化数据
* @dev: 请求初始化数据的设备
* @desc: 调节器描述信息
* @config: 调节器配置信息
* @node: 用于存储设备树节点的指针
*
* 从设备树中获取调节器的初始化数据,并为调节器配置填充信息。
* 返回一个指向填充后的初始化数据结构的指针,如果解析失败则返回 NULL。
*/
struct regulator_init_data *regulator_of_get_init_data(struct device *dev,
const struct regulator_desc *desc,
struct regulator_config *config,
struct device_node **node)
{
struct device_node *child;
struct regulator_init_data *init_data = NULL;
// 从设备树中获取与调节器描述匹配的节点
child = regulator_of_get_init_node(config->dev, desc);
if (!child)
return NULL;
// 调用 of_get_regulator_init_data 函数获取初始化数据
init_data = of_get_regulator_init_data(dev, child, desc);
if (!init_data) {
dev_err(dev, "failed to parse DT for regulator %pOFn\n", child);
goto error;
}
// 如果调节器描述中定义了回调函数,执行回调函数以进一步解析设备树
if (desc->of_parse_cb) {
int ret;
ret = desc->of_parse_cb(child, desc, config);
if (ret) {
if (ret == -EPROBE_DEFER) {
of_node_put(child);
return ERR_PTR(-EPROBE_DEFER);
}
dev_err(dev,
"driver callback failed to parse DT for regulator %pOFn\n",
child);
goto error;
}
}
// 将设备树节点的指针存储到传入的 node 指针中
*node = child;
return init_data;
error:
// 解析失败时释放设备树节点的引用
of_node_put(child);
return NULL;
}
总之,这段代码的作用是从设备树中获取与调节器描述匹配的节点,提取初始化数据,并执行相关的回调函数,以填充调节器的初始化信息和配置选项。这对于在Linux内核中配置和管理调节器非常重要,特别是在嵌入式系统中。
总结:1、2两种DTS解析的方法,各有优缺点:1直接,方便,容易理解,但会有冗余代码;2简洁,但需要regulator driver开发者非常熟悉解析的原理,并以此设计DTS和struct regulator_desc变量。大家可以根据实际情况,灵活使用。
4. 主要数据结构
4.1 struct regulator_desc
在注册regulator的时候,需要使用struct regulator_desc结构提供该regulator的静态描述。所谓的静态,是指这些描述不会在运行时改变,代表了设备的一种属性,如下:
/**
* struct regulator_desc - 静态调节器描述符
*
* 每个注册到核心的调节器都使用这种类型的结构体和 struct regulator_config 结构来描述。
* 这个结构包含调节器描述的不变部分。
*
* @name: 调节器的标识名称。
* @supply_name: 调节器供电的标识名称。
* @of_match: 在设备树中用于标识调节器的名称。
* @of_match_full_name: 标志,表示是否应与节点的 full_name 进行匹配。
* @regulators_node: 在设备树中包含调节器定义的节点的名称。
* @of_parse_cb: 可选的回调函数,仅在存在 of_match 时调用。
* 会在从设备树解析调节器数据时对每个调节器调用一次。
* 传递给回调函数的 regulator_config 是注册调节器时传递的副本,
* 仅对此特定调用有效。回调函数可以自由更改配置,但不能将其存储以供以后使用。
* 回调函数应返回成功的 0 或表示失败的负 ERRNO。
* @id: 调节器的数值标识符。
* @ops: 调节器操作表。
* @irq: 调节器的中断号。
* @type: 指示调节器是电压调节器还是电流调节器。
* @owner: 提供调节器的模块,用于引用计数。
*
* @continuous_voltage_range: 指示调节器是否可以在限定范围内设置任何电压。
* @n_voltages: ops.list_voltage() 可用的选择器数量。
* @n_current_limits: 电流限制的选择器数量。
*
* @min_uV: 最低选择器提供的电压(如果线性映射)。
* @uV_step: 每个选择器增加的电压(如果线性映射)。
* @linear_min_sel: 开始线性映射的最小选择器。
* @fixed_uV: 固定的电压值。
* @ramp_delay: 电压变化后稳定的时间(单位:uV/us)。
* @min_dropout_uV: 此调节器可处理的最小失压电压。
* @linear_ranges: 可能电压范围的常数表。
* @linear_range_selectors: 电压范围选择器的常数表。
* 如果使用可选择范围,则每个范围必须在此处有相应的选择器。
* @n_linear_ranges: @linear_ranges(和如果使用的话 @linear_range_selectors)表的条目数。
* @volt_table: 电压映射表(如果基于表的映射)。
* @curr_table: 电流限制映射表(如果基于表的映射)。
*
* @vsel_range_reg: 当使用可选择范围并且 ``regulator_map_*_voltage_*_pickable`` 函数时,
* 用于范围选择器的寄存器。
* @vsel_range_mask: 用于范围选择器的寄存器位字段的掩码。
* @vsel_reg: 当使用 ``regulator_map_*_voltage_*`` 时,用于选择器的寄存器。
* @vsel_mask: 用于选择器的寄存器位字段的掩码。
* @vsel_step: 设置电压时选择器步进分辨率的指定值。
* 如果为 0,则不进行步进(直接设置请求的选择器);如果 >0,则调节器 API 会逐渐递增/递减电压,每次增加/减少指定步进值。
* @csel_reg: 用于当前限制选择器的寄存器,使用 regmap set_current_limit。
* @csel_mask: 用于当前限制选择器的寄存器位字段的掩码。
* @apply_reg: 当使用 regulator_set_voltage_sel_regmap 时,用于启动输出电压更改的寄存器。
* @apply_bit: 当使用 regulator_set_voltage_sel_regmap 时,用于启动输出电压更改的寄存器位字段。
* @enable_reg: 当使用 regmap enable/disable 操作时,用于控制的寄存器。
* @enable_mask: 用于控制的寄存器位字段的掩码。
* @enable_val: 在使用 regmap enable/disable 操作时的启用值。
* @disable_val: 在使用 regmap enable/disable 操作时的禁用值。
* @enable_is_inverted: 标志,表示在使用 regulator_enable_regmap 和相关 API 时是否设置 enable_mask 位以禁用。
* @bypass_reg: 当使用 regmap set_bypass 时,用于控制的寄存器。
* @bypass_mask: 用于控制的寄存器位字段的掩码。
* @bypass_val_on: 在使用 regmap set_bypass 时的启用值。
* @bypass_val_off: 在使用 regmap set_bypass 时的禁用值。
* @active_discharge_off: 在使用 regmap set_active_discharge 时的启用值。
* @active_discharge_on: 在使用 regmap set_active_discharge 时的禁用值。
* @active_discharge_mask: 在使用 regmap set_active_discharge 时的控制位字段的掩码。
* @active_discharge_reg: 在使用 regmap set_active_discharge 时的控制寄存器。
* @soft_start_reg: 在使用 regmap set_soft_start 时的控制寄存器。
* @soft_start_mask: 在使用 regmap set_soft_start 时的控制位字段的掩码。
* @soft_start_val_on: 在使用 regmap set_soft_start 时的启用值。
* @pull_down_reg: 在使用 regmap set_pull_down 时的控制寄存器。
* @pull_down_mask: 在使用 regmap set_pull_down 时的控制位字段的掩码。
* @pull_down_val_on: 在使用 regmap set_pull_down 时的启用值。
*
* @ramp_reg: 控制调节器斜坡速率的寄存器。
* @ramp_mask: 斜坡速率控制寄存器的位掩码。
* @ramp_delay_table: 用于映射调节器斜坡速率值的表。值以 V/S(uV/uS)单位给出。
* @n_ramp_values: @ramp_delay_table 中的元素数。
*
* @enable_time: 初始启用调节器所需的时间(单位:微秒)。
* @off_on_delay: 在重新启用调节器之前的保护时间(单位:微秒)。
*
* @poll_enabled_time: 在检查调节器实际启用时使用的轮询间隔(单位:微秒),最大为 enable_time。
*
* @of_map_mode: 将设备树中定义的硬件模式映射到标准模式的函数。
*/
struct regulator_desc {
const char *name;
const char *supply_name;
const char *of_match;
bool of_match_full_name;
const char *regulators_node;
int (*of_parse_cb)(struct device_node *,
const struct regulator_desc *,
struct regulator_config *);
int id;
unsigned int continuous_voltage_range:1;
unsigned n_voltages;
unsigned int n_current_limits;
const struct regulator_ops *ops;
int irq;
enum regulator_type type;
struct module *owner;
unsigned int min_uV;
unsigned int uV_step;
unsigned int linear_min_sel;
int fixed_uV;
unsigned int ramp_delay;
int min_dropout_uV;
const struct linear_range *linear_ranges;
const unsigned int *linear_range_selectors;
int n_linear_ranges;
const unsigned int *volt_table;
const unsigned int *curr_table;
unsigned int vsel_range_reg;
unsigned int vsel_range_mask;
unsigned int vsel_reg;
unsigned int vsel_mask;
unsigned int vsel_step;
unsigned int csel_reg;
unsigned int csel_mask;
unsigned int apply_reg;
unsigned int apply_bit;
unsigned int enable_reg;
unsigned int enable_mask;
unsigned int enable_val;
unsigned int disable_val;
bool enable_is_inverted;
unsigned int bypass_reg;
unsigned int bypass_mask;
unsigned int bypass_val_on;
unsigned int bypass_val_off;
unsigned int active_discharge_on;
unsigned int active_discharge_off;
unsigned int active_discharge_mask;
unsigned int active_discharge_reg;
unsigned int soft_start_reg;
unsigned int soft_start_mask;
unsigned int soft_start_val_on;
unsigned int pull_down_reg;
unsigned int pull_down_mask;
unsigned int pull_down_val_on;
unsigned int ramp_reg;
unsigned int ramp_mask;
const unsigned int *ramp_delay_table;
unsigned int n_ramp_values;
unsigned int enable_time;
unsigned int off_on_delay;
unsigned int poll_enabled_time;
unsigned int (*of_map_mode)(unsigned int mode);
ANDROID_KABI_RESERVE(1);
};
这段代码定义了一个结构体 struct regulator_desc,用于描述调节器的静态属性和配置信息。这些信息包括调节器的名称、供电名称、设备树匹配信息、操作函数表等,以便在内核中注册和使用调节器时提供必要的配置和参数。该结构体的成员提供了对调节器硬件和功能的描述,以便内核正确管理和使用它们。
struct regulator_ops提供了regulator的所有操作,如下:
/**
* struct regulator_ops - 调节器操作。
*
* @enable: 配置调节器为启用状态。
* @disable: 配置调节器为禁用状态。
* @is_enabled: 如果调节器已启用则返回1,如果未启用则返回0。也可以返回负的错误码。
*
* @set_voltage: 在指定的范围内为调节器设置电压。驱动程序应选择最接近 min_uV 的电压。
* @set_voltage_sel: 使用指定的选择器为调节器设置电压。
* @map_voltage: 将电压转换为选择器。
* @get_voltage: 返回调节器的当前配置电压;如果调节器在启动时无法读取且尚未设置,则返回 -ENOTRECOVERABLE。
* @get_voltage_sel: 返回调节器的当前配置电压选择器;如果调节器在启动时无法读取且尚未设置,则返回 -ENOTRECOVERABLE。
* @list_voltage: 返回支持的一个电压值,单位为微伏;如果选择器指示的电压在此系统上不可用,则返回零;或者返回负的错误码。选择器的范围从零到 regulator_desc.n_voltages 减一。电压可以以任何顺序报告。
*
* @set_current_limit: 配置电流受限调节器的限制。驱动程序应选择最接近 max_uA 的电流。
* @get_current_limit: 获取电流受限调节器的配置限制。
* @set_input_current_limit: 配置输入限制。
*
* @set_over_current_protection: 支持启用和设置过流检测的限制。检测可以配置为三个不同严重性级别。
* - REGULATOR_SEVERITY_PROT 应自动关闭调节器。
* - REGULATOR_SEVERITY_ERR 应指示过流情况由不可恢复的错误引起,但硬件不会自动关闭。
* - REGULATOR_SEVERITY_WARN 应指示硬件仍然被认为未受损害,但需要进行板特定的恢复操作。如果 lim_uA 为 0,则不应更改限制,而只应根据请求启用/禁用检测。
*
* @set_over_voltage_protection: 支持启用和设置过压检测的限制。检测可以配置为与过流保护相同的严重性级别。单位为微伏。
* @set_under_voltage_protection: 支持启用和设置欠压检测的限制。检测可以配置为与过流保护相同的严重性级别。单位为微伏。
* @set_thermal_protection: 支持启用和设置过温度检测的限制。检测可以配置为与过流保护相同的严重性级别。单位为开尔文度。
*
* @set_active_discharge: 设置调节器的主动放电启用/禁用。
*
* @set_mode: 设置调节器的配置工作模式。
* @get_mode: 获取调节器的配置工作模式。
* @get_error_flags: 获取调节器的当前错误标志。
* @get_status: 返回调节器的实际状态(而非配置的状态),作为 REGULATOR_STATUS 值(或者负的错误码)。
* @get_optimum_mode: 获取在指定参数下运行时调节器的最高效工作模式。
* @set_load: 设置调节器的负载。
*
* @set_bypass: 设置调节器为旁路模式。
* @get_bypass: 获取调节器的旁路模式状态。
*
* @enable_time: 调节器输出电压在启用后稳定所需的时间,单位为微秒。
* @set_ramp_delay: 设置调节器的斜坡延迟。驱动程序应选择小于或等于 ramp_delay 的斜坡延迟。
* @set_voltage_time: 设置调节器的电压在被设置为新值后稳定所需的时间,单位为微秒。该函数接收从和到电压作为输入,应返回最坏情况。
* @set_voltage_time_sel: 设置调节器的电压在被设置为新的选择器后稳定所需的时间,单位为微秒。该函数接收从和到电压选择器作为输入,应返回最坏情况。
* @set_soft_start: 启用调节器的软启动。
*
* @set_suspend_voltage: 设置系统挂起时调节器的电压。
* @set_suspend_enable: 标记系统挂起时调节器为启用状态。
* @set_suspend_disable: 标记系统挂起时调节器为禁用状态。
* @set_suspend_mode: 设置系统挂起时调节器的工作模式。
* @resume: 恢复挂起的调节器的操作。
* @set_pull_down: 配置调节器在禁用时拉低。
*
* 该结构体描述了可以由调节器芯片驱动程序实现的调节器操作。
*/
struct regulator_ops {
int (*list_voltage) (struct regulator_dev *, unsigned selector);
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 *);
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 lim_uA, int severity, bool enable);
int (*set_over_voltage_protection)(struct regulator_dev *, int lim_uV, int severity, bool enable);
int (*set_under_voltage_protection)(struct regulator_dev *, int lim_uV, int severity, bool enable);
int (*set_thermal_protection)(struct regulator_dev *, int lim, int severity, bool enable);
int (*set_active_discharge)(struct regulator_dev *, bool enable);
int (*enable) (struct regulator_dev *);
int (*disable) (struct regulator_dev *);
int (*is_enabled) (struct regulator_dev *);
int (*set_mode) (struct regulator_dev *, unsigned int mode);
unsigned int (*get_mode) (struct regulator_dev *);
int (*get_error_flags)(struct regulator_dev *, unsigned int *flags);
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 *);
int (*get_status)(struct regulator_dev *);
unsigned int (*get_optimum_mode) (struct regulator_dev *, int input_uV, int output_uV, int load_uA);
int (*set_load)(struct regulator_dev *, int load_uA);
int (*set_bypass)(struct regulator_dev *dev, bool enable);
int (*get_bypass)(struct regulator_dev *dev, bool *enable);
};
这段代码定义了一个结构体 struct regulator_ops,用于描述调节器操作的函数指针集合。每个函数指针对应于不同的调节器操作,如设置电压、设置电流、启用/禁用等。这些函数指针定义了调节器驱动程序需要实现的操作,以便与内核交互。
调节器操作的功能包括配置调节器状态、设置电压和电流、获取当前状态、设置保护参数、设置工作模式等。这些操作用于管理和控制调节器的行为,以确保它们按预期工作并保护硬件免受潜在的损坏。
4.2 struct regulator_config
struct regulator_config保存了regulator的动态信息,所谓的动态信息,是指那些会在driver运行过程中改变、或者driver运行后才会确定的信息,如下:
/**
* struct regulator_config - 动态调节器描述符
*
* 每个向核心注册的调节器都使用此类型的结构体以及 struct regulator_desc 描述。
* 该结构体包含了调节器描述的运行时可变部分。
*
* @dev: 调节器的 struct device 结构体
* @init_data: 由平台提供的初始化数据,由驱动程序传递
* @driver_data: 私有的调节器数据
* @of_node: 用于解析设备树绑定的 OpenFirmware 节点(可以为 NULL)
* @regmap: 如果 dev_get_regmap() 不足够,用于核心 regmap 助手的 regmap
* @ena_gpiod: 控制调节器启用的 GPIO
*/
struct regulator_config {
struct device *dev; // 调节器所属设备的指针
const struct regulator_init_data *init_data; // 平台提供的初始化数据指针,由驱动程序传递
void *driver_data; // 私有的调节器数据指针
struct device_node *of_node; // 用于解析设备树绑定的 OpenFirmware 节点的指针(可以为 NULL)
struct regmap *regmap; // 用于核心 regmap 助手的 regmap 指针,如果 dev_get_regmap() 不足够
struct gpio_desc *ena_gpiod; // 控制调节器启用的 GPIO 描述符
};
这个结构体的目的是将调节器的运行时配置信息传递给调节器驱动程序,以便根据需要配置调节器的行为。
4.3 struct regulator_dev
struct regulator_dev是regulator设备的抽象,当driver以struct regulator_desc、struct regulator_config两个类型的参数,调用regulator_register将regulator注册到kernel之后,regulator就会分配一个struct regulator_dev变量,后续所有的regulator操作,都将以该变量为对象。
/**
* struct regulator_dev - 电压/电流调节器设备类
*
* 每个调节器都对应一个这样的结构体。这个结构体包含了调节器设备的描述信息。
* 除了调节器核心和通知注入之外,不应该直接由其他组件使用(应该获取互斥锁并且不进行其他直接访问)。
*/
struct regulator_dev {
const struct regulator_desc *desc; // 调节器的描述信息
int exclusive; // 互斥标志
u32 use_count; // 使用计数
u32 open_count; // 打开计数
u32 bypass_count; // 跳过计数
/* 我们所属的列表 */
struct list_head list; // 所有调节器的列表
/* 我们拥有的列表 */
struct list_head consumer_list; // 我们提供的消费者列表
struct coupling_desc coupling_desc; // 耦合描述
struct blocking_notifier_head notifier; // 通知器头部
struct ww_mutex mutex; // 互斥锁,用于保护消费者
struct task_struct *mutex_owner; // 持有互斥锁的任务
int ref_cnt; // 引用计数
struct module *owner; // 提供调节器的模块
struct device dev; // 调节器的设备
struct regulation_constraints *constraints; // 调节器的约束条件
struct regulator *supply; // 提供电源的调节器(供电调节器)
const char *supply_name; // 提供电源的调节器名称
struct regmap *regmap; // 用于 regmap 助手的 regmap
struct delayed_work disable_work; // 延迟工作队列,用于禁用调节器
void *reg_data; // 调节器设备数据
struct dentry *debugfs; // 调试文件系统的入口
struct regulator_enable_gpio *ena_pin; // 启用调节器的 GPIO
unsigned int ena_gpio_state:1; // 启用调节器的 GPIO 状态
unsigned int is_switch:1; // 开关调节器标志
/* 最后一次禁用调节器的时间 */
ktime_t last_off;
int cached_err; // 缓存的错误信息
bool use_cached_err; // 是否使用缓存的错误信息
spinlock_t err_lock; // 错误信息的自旋锁
ANDROID_KABI_RESERVE(1); // Android KABI 保留字段
};
5 实现逻辑分析
本章简单的分析一下regulator driver相关的实现逻辑。如果要理解有些逻辑,必须具备一些regulator的基础知识,因此在需要的时候,会穿插介绍这些知识。
5.1 regulator core的初始化
regulator core的初始化操作由regulator_init接口负责,主要工作包括:
/**
* regulator_init - 调节器子系统初始化函数
*/
static int __init regulator_init(void)
{
int ret;
// 注册调节器设备类
ret = class_register(®ulator_class);
// 创建用于调试的 debugfs 根目录
debugfs_root = debugfs_create_dir("regulator", NULL);
if (IS_ERR(debugfs_root))
pr_warn("regulator: Failed to create debugfs directory\n");
#ifdef CONFIG_DEBUG_FS
// 在 debugfs 中创建供电映射文件
debugfs_create_file("supply_map", 0444, debugfs_root, NULL,
&supply_map_fops);
// 在 debugfs 中创建调节器摘要文件
debugfs_create_file("regulator_summary", 0444, debugfs_root,
NULL, ®ulator_summary_fops);
#endif
// 初始化虚拟调节器
regulator_dummy_init();
// 注册通用调节器耦合器
regulator_coupler_register(&generic_regulator_coupler);
return ret;
}
/* 通过 core_initcall 将 regulator_init 设置为内核初始化函数,
以允许调节器子系统在系统引导时初始化。 */
core_initcall(regulator_init);
1)注册regulator class(/sys/class/regulator/)。
2)注册用于调试的debugfs。
和power switch class、input class等类似,regulator framework也是一种class,可以称作regulator class。
5.2 regulator register
regulator的注册,由regulator_register/devm_regulator_register接口负责,如下:
/**
* devm_regulator_register - 资源管理的 regulator_register()
* @dev: 需要供电的设备
* @regulator_desc: 要注册的调节器描述符
* @config: 调节器的运行时配置
*
* 调节器驱动程序调用此函数以注册一个调节器。在成功时返回指向 struct regulator_dev 的有效指针,
* 在错误时返回 ERR_PTR()。当设备解绑时,该调节器将自动释放。
*/
struct regulator_dev *devm_regulator_register(struct device *dev,
const struct regulator_desc *regulator_desc,
const struct regulator_config *config)
{
struct regulator_dev **ptr, *rdev;
// 为调节器设备分配 devres 结构
ptr = devres_alloc(devm_rdev_release, sizeof(*ptr),
GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
// 注册调节器,并将结果存储在 rdev 中
rdev = regulator_register(dev, regulator_desc, config);
// 如果注册成功,将 rdev 存储在 devres 结构中,并将 devres 结构添加到设备资源管理器
if (!IS_ERR(rdev)) {
*ptr = rdev;
devres_add(dev, ptr);
} else {
// 如果注册失败,释放之前分配的 devres 结构
devres_free(ptr);
}
// 返回注册结果,可能是成功的 regulator_dev 指针或错误指针(ERR_PTR)
return rdev;
}
EXPORT_SYMBOL_GPL(devm_regulator_register);
/**
* regulator_register - 注册调节器
* @dev: 驱动调节器的设备
* @regulator_desc: 要注册的调节器描述符
* @cfg: 调节器的运行时配置
*
* 由调节器驱动程序调用以注册调节器。成功时返回指向 struct regulator_dev 的有效指针,
* 错误时返回 ERR_PTR()。
*/
struct regulator_dev *
regulator_register(struct device *dev,
const struct regulator_desc *regulator_desc,
const struct regulator_config *cfg)
{
const struct regulator_init_data *init_data;
struct regulator_config *config = NULL;
static atomic_t regulator_no = ATOMIC_INIT(-1);
struct regulator_dev *rdev;
bool dangling_cfg_gpiod = false;
bool dangling_of_gpiod = false;
int ret, i;
bool resolved_early = false;
// 检查传入的运行时配置 cfg 是否为空,如果为空则返回错误指针
if (cfg == NULL)
return ERR_PTR(-EINVAL);
// 如果 cfg 中包含 ena_gpiod,则标记存在悬挂的配置 GPIO 描述符
if (cfg->ena_gpiod)
dangling_cfg_gpiod = true;
// 检查传入的调节器描述符 regulator_desc 是否为空,如果为空则返回错误
if (regulator_desc == NULL) {
ret = -EINVAL;
goto rinse;
}
// 对一些基本的合法性进行检查
WARN_ON(!dev || !cfg->dev);
if (regulator_desc->name == NULL || regulator_desc->ops == NULL) {
ret = -EINVAL;
goto rinse;
}
if (regulator_desc->type != REGULATOR_VOLTAGE &&
regulator_desc->type != REGULATOR_CURRENT) {
ret = -EINVAL;
goto rinse;
}
// 确保只实现了其中一个设置电压的函数,不应同时实现 set_voltage 和 set_voltage_sel
WARN_ON(regulator_desc->ops->get_voltage &&
regulator_desc->ops->get_voltage_sel);
WARN_ON(regulator_desc->ops->set_voltage &&
regulator_desc->ops->set_voltage_sel);
// 如果使用了电压选择器(voltage selector),则必须实现 list_voltage 函数
if (regulator_desc->ops->get_voltage_sel &&
!regulator_desc->ops->list_voltage) {
ret = -EINVAL;
goto rinse;
}
if (regulator_desc->ops->set_voltage_sel &&
!regulator_desc->ops->list_voltage) {
ret = -EINVAL;
goto rinse;
}
// 分配并初始化 regulator_dev 结构
rdev = kzalloc(sizeof(struct regulator_dev), GFP_KERNEL);
if (rdev == NULL) {
ret = -ENOMEM;
goto rinse;
}
device_initialize(&rdev->dev);
spin_lock_init(&rdev->err_lock);
// 复制配置以便驱动程序可以在解析 init 数据后覆盖它
config = kmemdup(cfg, sizeof(*cfg), GFP_KERNEL);
if (config == NULL) {
ret = -ENOMEM;
goto clean;
}
// 从设备树获取 init 数据,可能会推迟探测
init_data = regulator_of_get_init_data(dev, regulator_desc, config,
&rdev->dev.of_node);
if (PTR_ERR(init_data) == -EPROBE_DEFER) {
ret = -EPROBE_DEFER;
goto clean;
}
// 跟踪从设备树中解析的 GPIO 描述符,直到交给核心处理
if (!cfg->ena_gpiod && config->ena_gpiod)
dangling_of_gpiod = true;
if (!init_data) {
init_data = config->init_data;
rdev->dev.of_node = of_node_get(config->of_node);
}
// 初始化互斥锁
ww_mutex_init(&rdev->mutex, ®ulator_ww_class);
rdev->reg_data = config->driver_data;
rdev->owner = regulator_desc->owner;
rdev->desc = regulator_desc;
// 设置寄存器映射
if (config->regmap)
rdev->regmap = config->regmap;
else if (dev_get_regmap(dev, NULL))
rdev->regmap = dev_get_regmap(dev, NULL);
else if (dev->parent)
rdev->regmap = dev_get_regmap(dev->parent, NULL);
// 初始化链表
INIT_LIST_HEAD(&rdev->consumer_list);
INIT_LIST_HEAD(&rdev->list);
BLOCKING_INIT_NOTIFIER_HEAD(&rdev->notifier);
INIT_DELAYED_WORK(&rdev->disable_work, regulator_disable_work);
// 获取供应电压的名称
if (init_data && init_data->supply_regulator)
rdev->supply_name = init_data->supply_regulator;
else if (regulator_desc->supply_name)
rdev->supply_name = regulator_desc->supply_name;
// 注册到 sysfs
rdev->dev.class = ®ulator_class;
rdev->dev.parent = config->dev;
dev_set_name(&rdev->dev, "regulator.%lu",
(unsigned long) atomic_inc_return(®ulator_no));
dev_set_drvdata(&rdev->dev, rdev);
// 设置调节器约束
if (init_data)
rdev->constraints = kmemdup(&init_data->constraints,
sizeof(*rdev->constraints),
GFP_KERNEL);
else
rdev->constraints = kzalloc(sizeof(*rdev->constraints),
GFP_KERNEL);
if (!rdev->constraints) {
ret = -ENOMEM;
goto wash;
}
// 如果调节器需要解析供应电压,尝试在此时解析
if ((rdev->supply_name && !rdev->supply) &&
(rdev->constraints->always_on ||
rdev->constraints->boot_on)) {
ret = regulator_resolve_supply(rdev);
if (ret)
rdev_dbg(rdev, "unable to resolve supply early: %pe\n",
ERR_PTR(ret));
resolved_early = true;
}
// 如果存在初始化函数,执行调节器特定的初始化
if (init_data && init_data->regulator_init) {
ret = init_data->regulator_init(rdev->reg_data);
if (ret < 0)
goto wash;
}
// 如果配置中存在 ena_gpiod,则尝试请求使能 GPIO
if (config->ena_gpiod) {
ret = regulator_ena_gpio_request(rdev, config);
if (ret != 0) {
rdev_err(rdev, "Failed to request enable GPIO: %pe\n",
ERR_PTR(ret));
goto wash;
}
// 调节器核心接管了 GPIO 描述符
dangling_cfg_gpiod = false;
dangling_of_gpiod = false;
}
// 设置机器级别的约束
ret = set_machine_constraints(rdev);
if (ret == -EPROBE_DEFER && !resolved_early) {
/* 调节器可能处于旁路模式,因此需要其供应电压来设置约束 */
/* FIXME:这当前触发了在 sysfs 中创建 -SUPPLY 符号链接时的鸡生蛋问题 */
rdev_dbg(rdev, "will resolve supply early: %s\n",
rdev->supply_name);
ret = regulator_resolve_supply(rdev);
if (!ret)
ret = set_machine_constraints(rdev);
else
rdev_dbg(rdev, "unable to resolve supply early: %pe\n",
ERR_PTR(ret));
}
if (ret < 0)
goto wash;
// 初始化耦合(coupling)信息
ret = regulator_init_coupling(rdev);
if (ret < 0)
goto wash;
// 添加消费者设备
if (init_data) {
for (i = 0; i < init_data->num_consumer_supplies; i++) {
ret = set_consumer_device_supply(rdev,
init_data->consumer_supplies[i].dev_name,
init_data->consumer_supplies[i].supply);
if (ret < 0) {
dev_err(dev, "Failed to set supply %s\n",
init_data->consumer_supplies[i].supply);
goto unset_supplies;
}
}
}
// 如果不支持获取电压或列出电压,并且没有指定固定电压,则标记为开关调节器
if (!rdev->desc->ops->get_voltage &&
!rdev->desc->ops->list_voltage &&
!rdev->desc->fixed_uV)
rdev->is_switch = true;
// 添加调节器设备
ret = device_add(&rdev->dev);
if (ret != 0)
goto unset_supplies;
// 初始化调节器的 debugfs 接口
rdev_init_debugfs(rdev);
// 尝试解析与调节器耦合的调节器,因为新的调节器已注册
mutex_lock(®ulator_list_mutex);
regulator_resolve_coupling(rdev);
mutex_unlock(®ulator_list_mutex);
// 尝试解析调节器的供应电压,因为新的调节器已注册
class_for_each_device(®ulator_class, NULL, NULL,
regulator_register_resolve_supply);
kfree(config);
return rdev;
unset_supplies:
// 清除供应电压
mutex_lock(®ulator_list_mutex);
unset_regulator_supplies(rdev);
regulator_remove_coupling(rdev);
mutex_unlock(®ulator_list_mutex);
wash:
// 释放供应电压的引用
regulator_put(rdev->supply);
kfree(rdev->coupling_desc.coupled_rdevs);
// 释放使能 GPIO 相关资源
mutex_lock(®ulator_list_mutex);
regulator_ena_gpio_free(rdev);
mutex_unlock(®ulator_list_mutex);
// 释放调节器设备
put_device(&rdev->dev);
rdev = NULL;
clean:
// 如果存在悬挂的设备树 GPIO 描述符,则释放它
if (dangling_of_gpiod)
gpiod_put(config->ena_gpiod);
if (rdev && rdev->dev.of_node)
of_node_put(rdev->dev.of_node);
kfree(rdev);
kfree(config);
rinse:
// 如果存在悬挂的配置 GPIO 描述符,则释放它
if (dangling_cfg_gpiod)
gpiod_put(cfg->ena_gpiod);
// 返回错误码或注册成功的调节器设备指针
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(regulator_register);
这段代码的作用是允许调节器驱动程序在 Linux 内核中注册新的调节器,并设置其属性和约束条件,以便管理系统中的电压和电流。
注4:register map是kernel提供的一种管理寄存器的机制,特别是较为复杂的寄存器,如codec等。本文不会过多描述,如需要,会专门写一篇文章介绍该机制。
5.3 regulator的操作模式(operation mode)
regulator的主要功能,是输出电压/电流的调整(或改变)。由于模拟器件的特性,电压/电流的改变,是需要一定的时间的。对有些regulator而言,可以工作在不同的模式,这些模式有不同的改变速度,可想而知,较快的速度,有较大的功耗。下面是operation mode定义(位于include/linux/regulator/consumer.h中):
/*
* Regulator operating modes.
*
* Regulators can run in a variety of different operating modes depending on
* output load. This allows further system power savings by selecting the
* best (and most efficient) regulator mode for a desired load.
*
* Most drivers will only care about NORMAL. The modes below are generic and
* will probably not match the naming convention of your regulator data sheet
* but should match the use cases in the datasheet.
*
* In order of power efficiency (least efficient at top).
*
* Mode Description
* FAST Regulator can handle fast changes in it's load.
* e.g. useful in CPU voltage & frequency scaling where
* load can quickly increase with CPU frequency increases.
*
* NORMAL Normal regulator power supply mode. Most drivers will
* use this mode.
*
* IDLE Regulator runs in a more efficient mode for light
* loads. Can be used for devices that have a low power
* requirement during periods of inactivity. This mode
* may be more noisy than NORMAL and may not be able
* to handle fast load switching.
*
* STANDBY Regulator runs in the most efficient mode for very
* light loads. Can be used by devices when they are
* in a sleep/standby state. This mode is likely to be
* the most noisy and may not be able to handle fast load
* switching.
*
* NOTE: Most regulators will only support a subset of these modes. Some
* will only just support NORMAL.
*
* These modes can be OR'ed together to make up a mask of valid register modes.
*/
#define REGULATOR_MODE_INVALID 0x0
#define REGULATOR_MODE_FAST 0x1
#define REGULATOR_MODE_NORMAL 0x2
#define REGULATOR_MODE_IDLE 0x4
#define REGULATOR_MODE_STANDBY 0x8
相应的,regulator framework提供了一些机制,用于operation mode的操作,包括:
1)struct regulation_constraints中用于表示初始模式的字段initial_mode。
2)regulator ops中的set_mode/get_mode回调函数。
5.4 电压操作的两种方式
kernel抽象了两种电压操作的方法:
1)直接操作电压,对应struct regulator_ops中的如下回调函数:
1: /* get/set regulator voltage */
2: int (*list_voltage) (struct regulator_dev *, unsigned selector);
3: int (*set_voltage) (struct regulator_dev *, int min_uV, int max_uV,
4: unsigned *selector);
5: int (*get_voltage) (struct regulator_dev *);
其中set_voltage用于将电压设置为min_uV和max_uV范围内、和min_uV最接近的电压。该接口可以返回一个selector参数,用于告知调用者,实际的电压值;
get_voltage,用于返回当前的电压值;
list_voltage,以selector为参数,获取对应的电压值。
注5:有关selector的描述,可参考下面的介绍。
2)selector的形式
regulator driver以selector的形式,反映电压值。selector是一个从0开始的整数,driver提供如下的接口:
1: /* enumerate supported voltages */
2: int (*list_voltage) (struct regulator_dev *, unsigned selector);
3:
4: int (*map_voltage)(struct regulator_dev *, int min_uV, int max_uV);
5: int (*set_voltage_sel) (struct regulator_dev *, unsigned selector);
6: int (*get_voltage_sel) (struct regulator_dev *);
list_voltage,上面已经介绍;
map_voltage,是和list_voltage相对的接口,用于将电压范围map成一个selector值;
set_voltage_sel/get_voltage_sel,以selector的形式,操作电压。
regulator driver可以根据实际情况,选择一种实现方式。
5.5 regulator framework提供的sysfs接口
根据regulator提供的ops情况,regulator framework可以通过sysfs提供多种attribute,它们位于/sys/class/regulator/…/目录下,数量相当多,这里就不一一描述了,具体可参考:
https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-class-regulator