Linux regulator子系统是一个用于管理电源的框架,它允许内核控制和监测系统中的电源。该子系统提供了一种标准化的接口,使得设备驱动程序可以请求和控制电源,从而实现对系统功耗的优化。
在Linux内核中,每个电源都被表示为一个regulator对象。这些对象包含了电源的名称、电压、电流、电源状态等信息。驱动程序可以通过调用regulator API来请求和控制电源。
Linux regulator子系统还提供了一些特殊的regulator类型,例如fixed regulator和linear regulator。fixed regulator是一种固定电压的电源,它的电压是不可调的。而linear regulator则是一种可调电压的电源,它可以根据需要调整输出电压。
在Linux内核中,regulator子系统的实现是由一个核心框架和一系列驱动程序组成的。驱动程序负责与硬件交互,而核心框架则负责管理和调度这些驱动程序。
总的来说,Linux regulator子系统是一个非常重要的电源管理框架,它可以帮助开发人员实现对系统功耗的优化,从而提高系统的性能和稳定性。
提示:上一篇简单讲述内核设备树中电源的注册和引用,本篇讲讲内核电源驱动。
a) 注册GPIO开关电源
b) 注册GPIO可压电源
c) 注册PWM调压电源
d) 注册DC-DC电源
在 Linux 内核中,每个稳压器都需要在系统启动时进行注册。对于固定电压稳压器,注册过程非常简单,只需要调用 regulator_register_fixed_voltage() 函数即可。该函数定义在 drivers/regulator/fixed.c。
struct regulator_dev *regulator_register_fixed_voltage(struct device *dev,
const char *supply_name,
struct regulator_init_data *init_data,
unsigned int uV,
unsigned int min_uV,
unsigned int max_uV,
unsigned int n_voltages);
该函数的参数说明如下:
/ {
vcc5v0_otg: vcc5v0-otg-regulator {
compatible = "regulator-fixed";
regulator-name = "vcc5v0_otg";
//regulator-boot-on; //开机是自动开启供电
//regulator-always-on; //强制一直开启,不可被关闭
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
enable-active-high;
gpio = <&gpio2 RK_PA2 GPIO_ACTIVE_HIGH>;
vin-supply = <&vcc5v0_sys>;
pinctrl-names = "default";
pinctrl-0 = <&vcc5v0_otg_en>;
};
};
&pinctrl {
usb {
vcc5v0_otg_en: vcc5v0-otg-en {
rockchip,pins = <2 RK_PA2 RK_FUNC_GPIO &pcfg_pull_none>;
};
};
};
在 Linux 内核中,驱动程序的加载过程是由内核模块机制完成的。
可变压电源是一个 GPIO 控制的电源管理器,它可以通过 GPIO 控制电源的开关。在 Linux 内核中,它的源代码位于 drivers/regulator/gpio-regulator.c。
/ {
vccio_sd: vccio-sd-regulator {
compatible = "regulator-gpio";
regulator-name = "vccio_sd";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
gpios = <&gpio0 RK_PB6 GPIO_ACTIVE_HIGH>;
gpios-states = <0x1>;
states = <1800000 0x0
3300000 0x1>;
};
};
PWM regulator是一种基于PWM信号的电源调节器,它可以通过调节PWM信号的占空比来控制输出电压。在Linux内核中,PWM regulator的驱动程序位于drivers/regulator/pwm-regulator.c
。下面是PWM regulator的加载流程:
驱动程序初始化
在驱动程序初始化时,首先会调用pwm_regulator_probe函数。该函数会注册一个platform_driver结构体,并将其与pwm_regulator_driver结构体关联起来。同时,该函数还会调用pwm_regulator_of_match函数,用于匹配设备树中的节点信息。
设备树匹配
当系统启动时,内核会解析设备树,并将设备树中的节点信息传递给驱动程序。驱动程序会通过pwm_regulator_of_match函数匹配设备树中的节点信息,并将匹配到的节点信息保存在pwm_regulator_data结构体中。
/ {
vdd_log: vdd-logic {
compatible = "pwm-regulator";
rockchip,pwm_id = <1>;
rockchip,pwm_voltage = <1100000>;
pwms = <&pwm1 0 25000 1>;
regulator-name = "vcc_log";
regulator-min-microvolt = <860000>;
regulator-max-microvolt = <1360000>;
regulator-always-on;
regulator-boot-on;
};
};
PWM信号初始化
当驱动程序成功匹配到设备树中的节点信息后,会调用pwm_regulator_probe函数中的pwm_regulator_init函数,用于初始化PWM信号。该函数会调用pwm_get函数获取PWM信号,并设置PWM信号的频率和占空比。
电源调节器初始化
当PWM信号初始化完成后,会调用pwm_regulator_probe函数中的regulator_init函数,用于初始化电源调节器。该函数会调用regulator_register函数注册一个regulator_dev结构体,并将其与pwm_regulator_data结构体关联起来。同时,该函数还会调用regulator_set_voltage函数设置输出电压。
电源调节器使用
当电源调节器初始化完成后,就可以使用电源调节器了。在使用电源调节器时,可以调用regulator_enable和regulator_disable函数来启用和禁用电源调节器。同时,也可以调用regulator_set_voltage函数来设置输出电压。
以上就是PWM regulator的加载流程。在加载过程中,驱动程序会初始化PWM信号和电源调节器,并将它们关联起来。同时,驱动程序还会通过设备树匹配来获取节点信息。在使用电源调节器时,可以通过调用相关函数来控制输出电压。
以 TCS4525/6为例简单讲解电压调节器注册流程。tcs452x 内核驱动位置:drivers/regulator/fan53555.c。
static struct i2c_driver fan53555_regulator_driver = {
.driver = {
.name = "fan53555-regulator",
.of_match_table = of_match_ptr(fan53555_dt_ids),
},
.probe = fan53555_regulator_probe,
.shutdown = fan53555_regulator_shutdown,
.id_table = fan53555_id,
};
module_i2c_driver(fan53555_regulator_driver);
这里使用了 i2c_driver 结构体,其中包含了驱动程序的名称、设备树匹配表、probe 函数、shutdown 函数和 ID 表。最后,使用 module_i2c_driver 函数进行注册。
static const struct i2c_device_id fan53555_id[] = {
{
.name = "fan53555",
.driver_data = FAN53555_VENDOR_FAIRCHILD
}, {
.name = "rk8603",
.driver_data = FAN53555_VENDOR_RK
}, {
.name = "rk8604",
.driver_data = FAN53555_VENDOR_RK
}, {
.name = "syr827",
.driver_data = FAN53555_VENDOR_SILERGY
}, {
.name = "syr828",
.driver_data = FAN53555_VENDOR_SILERGY
}, {
.name = "tcs452x",
.driver_data = FAN53555_VENDOR_TCS
},
{ },
};
MODULE_DEVICE_TABLE(i2c, fan53555_id);
这里使用了 of_device_id 结构体,其中包含了设备的兼容性字符串。最后,使用 MODULE_DEVICE_TABLE 宏进行设备树匹配。
static int fan53555_regulator_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device_node *np = client->dev.of_node;
struct fan53555_device_info *di;
struct fan53555_platform_data *pdata;
struct regulator_config config = { };
unsigned int val;
int ret;
di = devm_kzalloc(&client->dev, sizeof(struct fan53555_device_info),
GFP_KERNEL);
if (!di)
return -ENOMEM;
di->desc.of_map_mode = fan53555_map_mode;
pdata = dev_get_platdata(&client->dev);
if (!pdata)
pdata = fan53555_parse_dt(&client->dev, np, &di->desc);//遍历设备树
if (!pdata || !pdata->regulator) {
dev_err(&client->dev, "Platform data not found!\n");
return -ENODEV;
}
di->vsel_gpio = pdata->vsel_gpio;
di->sleep_vsel_id = pdata->sleep_vsel_id;
di->regulator = pdata->regulator;
if (client->dev.of_node) {
di->vendor =
(unsigned long)of_device_get_match_data(&client->dev);
} else {
/* if no ramp constraint set, get the pdata ramp_delay */
if (!di->regulator->constraints.ramp_delay) {
int slew_idx = (pdata->slew_rate & 0x7)
? pdata->slew_rate : 0;
di->regulator->constraints.ramp_delay
= slew_rates[slew_idx];
}
di->vendor = id->driver_data;
}
di->regmap = devm_regmap_init_i2c(client, &fan53555_regmap_config);
if (IS_ERR(di->regmap)) {
dev_err(&client->dev, "Failed to allocate regmap!\n");
return PTR_ERR(di->regmap);
}
di->dev = &client->dev;
i2c_set_clientdata(client, di);
/* Get chip ID */
ret = regmap_read(di->regmap, FAN53555_ID1, &val);
if (ret < 0) {
dev_err(&client->dev, "Failed to get chip ID!\n");
return ret;
}
di->chip_id = val & DIE_ID;
/* Get chip revision */
ret = regmap_read(di->regmap, FAN53555_ID2, &val);
if (ret < 0) {
dev_err(&client->dev, "Failed to get chip Rev!\n");
return ret;
}
di->chip_rev = val & DIE_REV;
dev_info(&client->dev, "FAN53555 Option[%d] Rev[%d] Detected!\n",
di->chip_id, di->chip_rev);
/* Device init */
ret = fan53555_device_setup(di, pdata);
if (ret < 0) {
dev_err(&client->dev, "Failed to setup device!\n");
return ret;
}
/* Register regulator */
config.dev = di->dev;
config.init_data = di->regulator;
config.regmap = di->regmap;
config.driver_data = di;
config.of_node = np;
ret = fan53555_regulator_register(di, &config);
if (ret < 0)
dev_err(&client->dev, "Failed to register regulator!\n");
return ret;
}
static void fan53555_regulator_shutdown(struct i2c_client *client)
{
struct fan53555_device_info *di;
int ret;
di = i2c_get_clientdata(client);
dev_info(di->dev, "fan53555..... reset\n");
switch (di->vendor) {
case FAN53555_VENDOR_FAIRCHILD:
case FAN53555_VENDOR_RK:
case FAN53555_VENDOR_SILERGY:
ret = regmap_update_bits(di->regmap, di->slew_reg,
CTL_RESET, CTL_RESET);
break;
case FAN53555_VENDOR_TCS:
ret = regmap_update_bits(di->regmap, TCS452X_LIMCONF,
CTL_RESET, CTL_RESET);
/*
* the device can't return 'ack' during the reset,
* it will return -ENXIO, ignore this error.
*/
if (ret == -ENXIO)
ret = 0;
break;
default:
ret = -EINVAL;
break;
}
if (ret < 0)
dev_err(di->dev, "reset: force fan53555_reset error! ret=%d\n", ret);
else
dev_info(di->dev, "reset: force fan53555_reset ok!\n");
}
活学活用,做个合格的搬运工。