Linux regulator子系统分析4(基于Linux6.6)---regulator device驱动介绍
一、概述
调节器设备驱动与 Linux 电源管理的集成
调节器设备驱动通常与 Linux 内核的 电源管理框架(Power Management Framework)紧密集成,允许内核根据不同的工作负载来动态调整电源策略。
-
设备树支持:调节器硬件通常通过设备树(device tree)描述,在设备树中,调节器的硬件属性(如电压范围、电流限制等)会被明确声明。驱动程序需要解析这些设备树节点。
示例设备树配置:
dts
-
my_regulator: regulator@0 { compatible = "myvendor,my-regulator"; regulator-name = "my_regulator"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <3300000>; regulator-step-microvolt = <100000>; voltage-ranges = <1800000 3300000>; };
-
省电模式与动态调整:调节器设备驱动可以帮助系统实现动态电压调整和省电模式,以应对不同的负载需求。例如,当系统负载较低时,调节器可以通过降低电压来减少功耗。
-
调节器与其他电源管理组件的协同工作:调节器与 CPU频率调节器、内存管理器 等组件协作,以优化系统的整体电源消耗和性能。
调节器设备驱动的关键操作
-
启用调节器:
- 调节器需要在启动时启用,确保电压输出满足设备需求。
-
设置/获取电压:
- 驱动需要提供设置和读取电压的接口,确保电压在所需范围内。
-
电流限制:
- 对电流的管理可以帮助避免设备损坏,确保系统的稳定性。
-
禁用调节器:
- 在不需要电源时禁用调节器,以减少功耗。
二、 regulator device 驱动开发流程
- 创建struct regulator_desc类型的变量,包括regulator device的类型、寄存器定义、操作函数(struct regulator_ops类型的变量);
- 创建struct regulator_config类型的变量,包括regulator device相关的约束信息、consumer信息、regmap/enable gpio等相关信息;
- 调用regulator_register,完成regulator device的注册。
Regulator consumer的开发
- 通过regulator_get接口,依据supply name、device name在regulator_map查找是否存在对应的regulator_map信息,若存在则获取到对应的regulator_dev,并创建struct regulator 类型的变量;
- 通过regulator_get获取到struct regulator类型的变量后,则可以进行regulator的enable/disable等操作;
- 通过regulator_put释放regulator。
开发一个 regulator device 驱动,通常涉及到调节器设备的注册、配置、管理以及与其他设备(如电源供应、外设等)之间的协调。为了开发一个调节器驱动,开发者需要理解调节器框架的结构、如何使用相关 API 来实现电压、电流等控制功能,以及如何在 Linux 内核中进行设备注册和管理。以下是开发调节器驱动的基本流程。
1. 了解调节器子系统
调节器子系统提供了一个统一的接口来管理系统中的电源调节器设备,主要功能包括:
- 控制电源调节器的电压、当前输出和工作模式。
- 启用和禁用调节器。
- 调节器的状态管理和依赖关系处理。
调节器驱动通常是通过实现调节器设备的具体控制函数,并将其注册到调节器框架来工作。
2. 定义调节器设备(regulator_dev
)
调节器设备通常由硬件平台或电源管理 IC 提供,每个调节器设备通常会有多个工作模式、电压范围和电流限制。调节器驱动需要定义调节器的操作行为和相关参数。
3. 创建调节器驱动的步骤
定义调节器设备的操作
首先需要定义一个调节器设备操作结构体,这个结构体包含了调节器的各种操作函数,如获取电压、电流、启用/禁用调节器等。
static const struct regulator_ops my_regulator_ops = {
.enable = my_regulator_enable,
.disable = my_regulator_disable,
.get_voltage = my_regulator_get_voltage,
.set_voltage = my_regulator_set_voltage,
.get_current_limit = my_regulator_get_current_limit,
.set_current_limit = my_regulator_set_current_limit,
};
enable
:启用调节器。disable
:禁用调节器。get_voltage
:获取当前输出电压。set_voltage
:设置输出电压。get_current_limit
:获取当前输出电流限制。set_current_limit
:设置输出电流限制。
定义调节器设备的描述信息
接下来,需要定义调节器的描述信息,包括名称、电压范围、电流限制等。这些信息将在调节器驱动注册时传入:
static struct regulator_desc my_regulator_desc = {
.name = "my_regulator",
.of_match = my_regulator_of_match, // 设备树匹配
.ops = &my_regulator_ops,
.type = REGULATOR_VOLTAGE, // 电压调节器
.id = -1, // 调节器 ID
.n_voltages = 2, // 支持的电压数量
.min_uV = 1800000, // 最小电压
.uV_step = 100000, // 电压步进
};
name
:调节器的名称,通常是硬件设备的名字。of_match
:设备树的匹配表(如果使用设备树)。ops
:调节器操作结构体。type
:调节器的类型,通常是电压调节器或电流调节器。n_voltages
:支持的电压数量。min_uV
和uV_step
:电压的最小值和步进值。
初始化调节器设备
调节器设备需要在驱动的初始化函数中进行注册。通过 regulator_register()
注册调节器并初始化。
static int my_regulator_probe(struct platform_device *pdev)
{
struct regulator_dev *rdev;
int ret;
rdev = regulator_register(&my_regulator_desc, &pdev->dev);
if (IS_ERR(rdev)) {
ret = PTR_ERR(rdev);
dev_err(&pdev->dev, "Failed to register regulator: %d\n", ret);
return ret;
}
platform_set_drvdata(pdev, rdev);
return 0;
}
在 probe()
函数中,调用 regulator_register()
来注册调节器设备。如果注册成功,rdev
结构体将表示该调节器设备,后续的操作可以通过该结构体来完成。
释放调节器设备资源
当驱动程序卸载或者设备不再需要时,需要释放调节器资源,通常通过 regulator_unregister()
来注销调节器设备。
static int my_regulator_remove(struct platform_device *pdev)
{
struct regulator_dev *rdev = platform_get_drvdata(pdev);
regulator_unregister(rdev);
return 0;
}
在 remove()
函数中,调用 regulator_unregister()
来注销调节器。
三、regulator device driver设计
设计一个 regulator device driver(调节器设备驱动)是 Linux 内核电源管理领域的一个关键任务。调节器(regulator)在电源管理中扮演着非常重要的角色,通常用于调节电压和电流,以确保硬件设备的正常运行。在开发调节器驱动时,需要处理硬件平台的具体实现,同时遵循内核的电源管理框架,确保设备能够正确地调节电压、启用、禁用以及管理电流等。
以下是设计一个调节器设备驱动的详细步骤和结构:
3.1、调节器框架简介
调节器框架是 Linux 内核中的一个子系统,允许设备在运行时调整电压和电流,主要作用是电源管理。调节器通过统一的接口向上层提供电压、电流调节等功能,底层则是与具体硬件平台的电源管理芯片(PMIC,Power Management IC)通信。
- 调节器子系统通过
regulator
设备类进行操作,通常包括以下操作:- 启用/禁用调节器
- 设置/获取电压
- 设置/获取电流
- 获取调节器的状态
3.2、调节器设备的结构
调节器设备通常由以下几个部分组成:
- 调节器操作结构体(
regulator_ops
):包含对调节器设备的基本操作函数。 - 调节器描述符(
regulator_desc
):描述调节器设备的属性,如电压范围、支持的电压数量等。 - 调节器设备结构体(
regulator_dev
):表示一个调节器设备实例。
3.3、调节器设备驱动的关键设计步骤
1.定义调节器操作
调节器操作是调节器驱动的核心,它们定义了调节器设备的行为。一个调节器设备需要支持的一些常见操作包括:
- 启用(
enable
) - 禁用(
disable
) - 获取电压(
get_voltage
) - 设置电压(
set_voltage
) - 获取电流(
get_current_limit
) - 设置电流(
set_current_limit
)
例如:
static const struct regulator_ops my_regulator_ops = {
.enable = my_regulator_enable, // 启用调节器
.disable = my_regulator_disable, // 禁用调节器
.get_voltage = my_regulator_get_voltage, // 获取电压
.set_voltage = my_regulator_set_voltage, // 设置电压
.get_current_limit = my_regulator_get_current_limit, // 获取电流限制
.set_current_limit = my_regulator_set_current_limit, // 设置电流限制
};
每个操作函数的实现将根据硬件平台的具体需求来定义。
2.定义调节器描述符(regulator_desc
)
调节器描述符结构体是调节器的配置项,定义了调节器的各种属性,如电压范围、电流限制、调节器类型等。
例如:
static struct regulator_desc my_regulator_desc = {
.name = "my_regulator",
.of_match = my_regulator_of_match, // 设备树匹配表
.ops = &my_regulator_ops, // 调节器操作
.type = REGULATOR_VOLTAGE, // 电压调节器
.id = -1, // 调节器 ID
.n_voltages = 3, // 支持的电压数量
.min_uV = 1800000, // 最小电压(单位微伏)
.max_uV = 3300000, // 最大电压(单位微伏)
.uV_step = 100000, // 电压步进(单位微伏)
};
3.初始化调节器设备
调节器设备在驱动的 probe
函数中进行初始化和注册。regulator_register
函数用于将调节器设备注册到调节器子系统。
static int my_regulator_probe(struct platform_device *pdev)
{
struct regulator_dev *rdev;
int ret;
rdev = regulator_register(&my_regulator_desc, &pdev->dev);
if (IS_ERR(rdev)) {
ret = PTR_ERR(rdev);
dev_err(&pdev->dev, "Failed to register regulator: %d\n", ret);
return ret;
}
platform_set_drvdata(pdev, rdev);
return 0;
}
regulator_register
会根据 regulator_desc
中的配置信息注册调节器设备。
4.释放调节器设备
在设备被卸载或不再使用时,需要释放调节器设备的资源。可以在 remove
函数中通过 regulator_unregister
来注销调节器。
static int my_regulator_remove(struct platform_device *pdev)
{
struct regulator_dev *rdev = platform_get_drvdata(pdev);
regulator_unregister(rdev); // 注销调节器设备
return 0;
}
5.设备树支持(可选)
如果调节器硬件由设备树描述,驱动需要支持设备树解析。设备树提供了硬件平台描述信息,驱动通过设备树节点来获取调节器的属性。
设备树节点示例:
dts
my_regulator: regulator@0 {
compatible = "myvendor,my-regulator";
regulator-name = "my_regulator";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
regulator-step-microvolt = <100000>;
voltage-ranges = <1800000 3300000>;
};
设备树匹配表:
static const struct of_device_id my_regulator_of_match[] = {
{ .compatible = "myvendor,my-regulator", },
{ }
};
MODULE_DEVICE_TABLE(of, my_regulator_of_match);
在驱动的 probe
函数中,使用 of_match_device
函数匹配设备树中的设备节点,并初始化调节器。
6.调节器资源管理
调节器设备的资源管理非常重要。调节器驱动不仅要能够调节电压和电流,还要确保调节器的状态正确管理。例如:
- 启用/禁用调节器:在启用时,需要确保调节器的电压已设置为所需的电压值,禁用时需要关闭调节器的输出。
- 电压设置:需要确保设置的电压在硬件支持的范围内,并且在多个设备共用一个调节器时,能够正确管理电压和功耗。
调节器的 enable
和 disable
操作需要确保对硬件状态的正确控制。
static int my_regulator_enable(struct regulator_dev *rdev)
{
/* 控制硬件启用调节器 */
return 0;
}
static int my_regulator_disable(struct regulator_dev *rdev)
{
/* 控制硬件禁用调节器 */
return 0;
}
7.调试与测试
调节器驱动开发完成后,需要进行全面的测试,确保调节器能够根据需求进行电压调节、启用/禁用等操作。可以通过内核日志输出(如 pr_debug
)来调试和验证驱动的行为。
例如,在启用调节器时:
pr_debug("Enabling regulator %s\n", rdev->desc->name);
8.内核模块的集成
完成调节器驱动的设计和实现后,将其作为内核模块加载和集成。确保模块的依赖关系正确,及时加载和卸载模块。
static struct platform_driver my_regulator_driver = {
.probe = my_regulator_probe,
.remove = my_regulator_remove,
.driver = {
.name = "my-regulator",
.of_match_table = my_regulator_of_match,
},
};
module_platform_driver(my_regulator_driver);
MODULE_LICENSE("GPL");
3.4、总结
设计一个调节器设备驱动需要完成多个步骤,从定义调节器的操作到硬件资源的管理。关键步骤包括:
- 定义调节器操作结构体(
regulator_ops
)。 - 创建调节器描述符结构体(
regulator_desc
)。 - 在
probe
函数中注册调节器设备,并在remove
函数中注销调节器设备。 - 根据硬件需求实现调节器的启用、禁用、设置电压、电流等操作。
- 支持设备树(可选)以便硬件描述与驱动程序匹配。
- 对驱动进行调试、测试和优化,确保其稳定性和兼容性。