Linux 内核之电源篇(加载流程)

Linux 内核 regulator子系统(启动流程)

Linux regulator子系统是一个用于管理电源的框架,它允许内核控制和监测系统中的电源。该子系统提供了一种标准化的接口,使得设备驱动程序可以请求和控制电源,从而实现对系统功耗的优化。

在Linux内核中,每个电源都被表示为一个regulator对象。这些对象包含了电源的名称、电压、电流、电源状态等信息。驱动程序可以通过调用regulator API来请求和控制电源。
Linux regulator子系统还提供了一些特殊的regulator类型,例如fixed regulator和linear regulator。fixed regulator是一种固定电压的电源,它的电压是不可调的。而linear regulator则是一种可调电压的电源,它可以根据需要调整输出电压。
在Linux内核中,regulator子系统的实现是由一个核心框架和一系列驱动程序组成的。驱动程序负责与硬件交互,而核心框架则负责管理和调度这些驱动程序。
总的来说,Linux regulator子系统是一个非常重要的电源管理框架,它可以帮助开发人员实现对系统功耗的优化,从而提高系统的性能和稳定性。


提示:上一篇简单讲述内核设备树中电源的注册和引用,本篇讲讲内核电源驱动。

文章目录

  • Linux 内核 regulator子系统(启动流程)
  • 圈重点 看想学
  • 1 注册GPIO控制开关电源
  • 2 注册GPIO控制可变压电源
  • 3 注册PWM可调压电源
  • 4 注册DC-DC电源
  • Tips
  • 总结


圈重点 看想学

a) 注册GPIO开关电源
b) 注册GPIO可压电源
c) 注册PWM调压电源
d) 注册DC-DC电源


1 注册GPIO控制开关电源

在 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);

该函数的参数说明如下:

  • dev:指向该稳压器所属设备的指针。
  • supply_name:该稳压器的名称。
  • init_data:指向 regulator_init_data 结构体的指针,该结构体包含了该稳压器的初始化数据。
  • uV:该稳压器的输出电压。
  • min_uV:该稳压器的最小输出电压。
  • max_uV:该稳压器的最大输出电压。
  • n_voltages:该稳压器支持的电压档位数量。
    在注册固定电压稳压器时,我们只需要指定 uV、min_uV 和 max_uV 三个参数即可,因为固定电压稳压器只支持一个电压档位。
/ {
       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 内核中,驱动程序的加载过程是由内核模块机制完成的。

  1. 驱动程序注册
    在驱动程序初始化时,调用 platform_driver_register() 函数来注册驱动程序。这个函数会将驱动程序添加到 platform 驱动程序列表中。
  2. 设备匹配
    当系统启动时,内核会扫描设备树来匹配设备和驱动程序。当匹配到一个与 regulator-fixed 驱动程序匹配的设备时,内核会调用驱动程序的 probe() 函数。
  3. probe() 函数
    probe() 函数会执行以下操作:
  • 调用 devm_regulator_register_fixed_voltage() 函数来注册固定电压的电源。
  • 设置电源的名称、电压和电流限制等属性。
  • 返回 0 表示成功。
  1. 设备卸载
    当系统关闭或者设备被移除时,内核会调用驱动程序的 remove() 函数来卸载设备。
  2. remove() 函数
    remove() 函数会执行以下操作:
  • 调用 devm_regulator_unregister() 函数来注销电源。
  • 返回 0 表示成功。

2 注册GPIO控制可变压电源

可变压电源是一个 GPIO 控制的电源管理器,它可以通过 GPIO 控制电源的开关。在 Linux 内核中,它的源代码位于 drivers/regulator/gpio-regulator.c。

  1. 在设备树中定义 regulator-gpio 节点,包括 GPIO 端口号、电源名称、电源类型等信息。
/ {
	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>;
	};
};
  1. 在内核启动时,通过调用 of_register_platform_driver() 函数注册 platform_driver,将 regulator-gpio 的驱动程序注册到内核中。
  2. 当系统启动时,内核会自动扫描设备树,找到 regulator-gpio 节点,并将其与驱动程序进行匹配。
  3. 当匹配成功后,内核会调用 platform_driver 中的 probe() 函数,该函数会初始化 regulator-gpio 并注册到内核的电源管理器中。
  4. 在 probe() 函数中,首先会获取 regulator-gpio 节点中的 GPIO 端口号,并通过调用 gpio_request() 函数请求该 GPIO 端口。
  5. 然后,通过调用 regulator_register() 函数将 regulator-gpio 注册到内核的电源管理器中。
  6. 当需要控制电源时,应用程序可以通过调用 regulator_get() 函数获取 regulator-gpio 的句柄,然后通过调用 regulator_enable() 或 regulator_disable() 函数来控制电源的开关。
  7. 当不再需要使用 regulator-gpio 时,应用程序可以通过调用 regulator_put() 函数释放该句柄,并通过调用 regulator_unregister() 函数将 regulator-gpio 从内核的电源管理器中注销。

3 注册PWM可调压电源

PWM regulator是一种基于PWM信号的电源调节器,它可以通过调节PWM信号的占空比来控制输出电压。在Linux内核中,PWM regulator的驱动程序位于drivers/regulator/pwm-regulator.c。下面是PWM regulator的加载流程:

  1. 驱动程序初始化
    在驱动程序初始化时,首先会调用pwm_regulator_probe函数。该函数会注册一个platform_driver结构体,并将其与pwm_regulator_driver结构体关联起来。同时,该函数还会调用pwm_regulator_of_match函数,用于匹配设备树中的节点信息。

  2. 设备树匹配
    当系统启动时,内核会解析设备树,并将设备树中的节点信息传递给驱动程序。驱动程序会通过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;
	};
};
  1. PWM信号初始化
    当驱动程序成功匹配到设备树中的节点信息后,会调用pwm_regulator_probe函数中的pwm_regulator_init函数,用于初始化PWM信号。该函数会调用pwm_get函数获取PWM信号,并设置PWM信号的频率和占空比。

  2. 电源调节器初始化
    当PWM信号初始化完成后,会调用pwm_regulator_probe函数中的regulator_init函数,用于初始化电源调节器。该函数会调用regulator_register函数注册一个regulator_dev结构体,并将其与pwm_regulator_data结构体关联起来。同时,该函数还会调用regulator_set_voltage函数设置输出电压。

  3. 电源调节器使用
    当电源调节器初始化完成后,就可以使用电源调节器了。在使用电源调节器时,可以调用regulator_enable和regulator_disable函数来启用和禁用电源调节器。同时,也可以调用regulator_set_voltage函数来设置输出电压。

以上就是PWM regulator的加载流程。在加载过程中,驱动程序会初始化PWM信号和电源调节器,并将它们关联起来。同时,驱动程序还会通过设备树匹配来获取节点信息。在使用电源调节器时,可以通过调用相关函数来控制输出电压。

4 注册DC-DC电源

以 TCS4525/6为例简单讲解电压调节器注册流程。tcs452x 内核驱动位置:drivers/regulator/fan53555.c。

  1. 驱动注册
    在 Linux 中,驱动程序需要通过注册函数进行注册,以便系统能够识别和加载它们。在 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 函数进行注册。

  1. 设备树匹配
    在 Linux 中,设备树是描述硬件设备的一种标准化方式。驱动程序需要使用设备树匹配来确定哪些设备需要加载该驱动程序。在 fan53555.c 中,使用以下代码进行设备树匹配:
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 宏进行设备树匹配。

  1. Probe 函数
    在 Linux 中,probe 函数用于初始化驱动程序并将其与硬件设备关联。在 fan53555.c 中,使用以下代码进行 probe 函数:
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;
}
  1. 驱动卸载
    由于是重要电压调节器,不允许被卸载;关机关联函数替代恰到好处。在 fan53555.c 中,使用以下代码进行 shutdown 函数:
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");
}

Tips

总结

活学活用,做个合格的搬运工。

你可能感兴趣的:(Android,PMIC,Linux,linux,android)