linux内核regulator驱动子系统

文章目录

  • 概述
  • 一、provider驱动
    • [1.1 设备树](#1.1 设备树)
    • [1.2 数据结构](#1.2 数据结构)
      • [1.2.1 struct regulator_desc](#1.2.1 struct regulator_desc)
      • 1.2.2
      • [1.2.3 struct regulator_ops](#1.2.3 struct regulator_ops)
    • [1.3 代码分析](#1.3 代码分析)
      • [1.3.1 scmi设备树](#1.3.1 scmi设备树)
      • [1.3.2 probe驱动代码](#1.3.2 probe驱动代码)
      • [1.3.3 process_scmi_regulator_of_node](#1.3.3 process_scmi_regulator_of_node)
      • [1.3.4 scmi_regulator_common_init](#1.3.4 scmi_regulator_common_init)
      • [1.3.5 devm_regulator_register](#1.3.5 devm_regulator_register)
  • 二、consumer驱动
    • [2.1 设备树](#2.1 设备树)
    • [2.2 api函数](#2.2 api函数)
      • [2.2.1 获取regulator接口](#2.2.1 获取regulator接口)
      • [2.2.2 释放regulator接口](#2.2.2 释放regulator接口)
      • [2.2.3 regulator控制和状态接口](#2.2.3 regulator控制和状态接口)
      • [2.2.4 多regulator控制接口](#2.2.4 多regulator控制接口)
平台 内核版本
ARM linux 5.15

概述

Regulator 子系统是 Linux 内核中用于管理系统电源稳压器的框架,它为核心、驱动和用户空间提供了统一的电压/电流调节接口。该子系统抽象了各类稳压器硬件(如 PMIC、低压差稳压器),通过 consumer、provider 和 core 三层结构,实现动态电压/频率调节(DVFS)、电源域管理以及睡眠状态下的功耗优化。对于一款SOC芯片来说,精细化地控制电源开关、电压、电流输出虽然复杂度较高,但作为对功耗有显著影响的模块,还是值得细研究下。一般开发板是通过外接PMIC芯片进行电压调节的,比如瑞芯微使用的rk808,我们在这块芯片的驱动可以看到regulator的一些端倪。下面一起分析regulator设计及实现。

一、provider驱动

1.1 设备树

我们先看看固定电压和rk808的设备树:

c 复制代码
vcc_sys: vcc-sys-regulator {
	compatible = "regulator-fixed";
	regulator-name = "vcc_sys";
	regulator-min-microvolt = <5000000>;
	regulator-max-microvolt = <5000000>;
	regulator-always-on;
	regulator-boot-on;
};

&i2c0 {
	rk808: pmic@1b {
		compatible = "rockchip,rk808";
		reg = <0x1b>;
		vcc1-supply = <&vcc_sys>;

		regulators {
			vdd_cpu: DCDC_REG1 {
				regulator-always-on;
				regulator-boot-on;
				regulator-min-microvolt = <700000>;
				regulator-max-microvolt = <1500000>;
				regulator-name = "vdd_cpu";
			};
		
			vdd_log: DCDC_REG2 {
				regulator-always-on;
				regulator-boot-on;
				regulator-min-microvolt = <700000>;
				regulator-max-microvolt = <1500000>;
				regulator-name = "vdd_log";
			};
			...
		}
	}
}

1.2 数据结构

1.2.1 struct regulator_desc

struct regulator_desc 是 Linux Regulator 子系统的核心数据结构之一,用于在驱动中静态地描述一个稳压器的固有硬件特性,也可以说是一个电压或者电流源的对象。也是是为regulator_register()函数提供注册一个稳压器所需的非可变硬件信息数据结构。结构体很大,因为记录支持电压电流的方式不同,有线性的,有散列的,还有固定的。

c 复制代码
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 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;

};

这个结构体最重要的成员主要是记录了支持哪些电压和操作硬件的ops。

1.2.2

regulator_config 作用类似于安装配置包,将稳压器的静态能力(regulator_desc)与具体运行环境(哪个设备、什么约束、如何访问硬件)绑定,是驱动调用 regulator_register() 时必须提供的另一个参数。

c 复制代码
struct regulator_config {
	struct device *dev;//指向拥有此稳压器的父设备对象
	const struct regulator_init_data *init_data;//指向初始化约束数据
	void *driver_data;//驱动私有数据指针
	struct device_node *of_node;//指向关联的设备树节点
	struct regmap *regmap;//寄存器访问抽象句柄(可选)
	struct gpio_desc *ena_gpiod;//GPIO控制使能引脚(可选)
};

1.2.3 struct regulator_ops

struct regulator_ops 是 Regulator 子系统中由硬件驱动实现的核心操作函数集,它定义了稳压器所有可能的硬件控制动作(如使能、设置电压),是连接框架抽象与具体硬件的桥梁。该结构体包含一系列函数指针(如 enable, set_voltage_sel, get_voltage),驱动开发者根据芯片实际能力进行填充;框架和消费者调用这些统一接口时,驱动通过底层的寄存器读写来执行真实操作,从而实现了硬件差异的屏蔽与电源管理的标准化。

c 复制代码
static const struct regulator_ops scmi_reg_discrete_ops = {
	.enable = scmi_reg_enable,//启用稳压器输出
	.disable = scmi_reg_disable,//禁用稳压器输出
	.is_enabled = scmi_reg_is_enabled,//查询稳压器当前启用状态
	.get_voltage_sel = scmi_reg_get_voltage_sel,//获取当前电压选择器索引值
	.set_voltage_sel = scmi_reg_set_voltage_sel,//设置电压选择器索引值
	.list_voltage = regulator_list_voltage_table,//查询所有支持的电压值
	.map_voltage = regulator_map_voltage_iterate,//将电压值转换为对应的索引值
};

这里是比较重要的几个成员,其中要注意的是get_voltage_sel和set_voltage_sel成员传入参数并不是电压值,而已电压在regulator的索引值。

1.3 代码分析

1.3.1 scmi设备树

c 复制代码
firmware {
	scmi: scmi {
		compatible = "arm,scmi-smc";
		arm,smc-id = <0x82000010>;
		shmem = <&scmi_shmem>;
		#address-cells = <1>;
		#size-cells = <0>;

		scmi_regu: regulators {
			#address-cells = <1>;
			#size-cells = <0>;
	
			scmi_reg_dcdc1: scmi_dcdc1 {
				reg = <1>;
				regulator-name = "dcdc1";
			};
	
			scmi_reg_dcdc2: scmi_dcdc2 {
				reg = <2>;
				regulator-name = "dcdc2";
			};
		};
	};
};

1.3.2 probe驱动代码

在代码分析这一块,我们还是看scmi regulator驱动,因为这里没有哪些复杂的硬件初始化和操作,方便我们分析,代码在drivers/regulator/scmi-regulator.c。

下面是驱动的probe伪代码:

c 复制代码
static int scmi_regulator_probe(struct scmi_device *sdev)
{
	int d, ret, num_doms;
	struct device_node *np, *child;
	const struct scmi_handle *handle = sdev->handle;
	struct scmi_regulator_info *rinfo;
	struct scmi_protocol_handle *ph;

	num_doms = voltage_ops->num_domains_get(ph);

	rinfo = devm_kzalloc(&sdev->dev, sizeof(*rinfo), GFP_KERNEL);

	rinfo->sregv = devm_kcalloc(&sdev->dev, num_doms,
				    sizeof(void *), GFP_KERNEL);
	rinfo->num_doms = num_doms;

	np = of_find_node_by_name(handle->dev->of_node, "regulators");
	for_each_child_of_node(np, child) {
		ret = process_scmi_regulator_of_node(sdev, ph, child, rinfo);
	}

	for (d = 0; d < num_doms; d++) {
		struct scmi_regulator *sreg = rinfo->sregv[d];
		/* Skip empty slots */
		if (!sreg)
			continue;
		ret = scmi_regulator_common_init(sreg);

		sreg->rdev = devm_regulator_register(&sdev->dev, &sreg->desc,
						     &sreg->conf);
	}

	dev_set_drvdata(&sdev->dev, rinfo);

	return 0;
}

probe函数很简单,主要是做了以下几件事:

  1. 调用scmi协议函数voltage_ops->num_domains_get查询host支持regulator的数量,
  2. 调用函数of_find_node_by_name找到该设备下的regulators节点,
  3. 调用函数遍历regulators节点下的每一个子节点,也就是设备树中提供的电压节点,对每一个节点调用函数process_scmi_regulator_of_node初始化,
  4. 遍历scmi支持的regulator,如果我们设备树没有使用到这个regulator,就跳过他;否则就调用函数scmi_regulator_common_init函数初始化,最后调用函数devm_regulator_register注册。

后面我们再看看process_scmi_regulator_of_node、scmi_regulator_common_init和devm_regulator_register函数。

1.3.3 process_scmi_regulator_of_node

process_scmi_regulator_of_node函数主要是根据regulator节点通过分配私有成员的内存和记录一些简单的信息:

c 复制代码
static int process_scmi_regulator_of_node(struct scmi_device *sdev,
					  struct scmi_protocol_handle *ph,
					  struct device_node *np,
					  struct scmi_regulator_info *rinfo)
{
	u32 dom, ret;

	ret = of_property_read_u32(np, "reg", &dom);
	if (ret)
		return ret;

	if (dom >= rinfo->num_doms)
		return -ENODEV;

	if (rinfo->sregv[dom]) {
		dev_err(&sdev->dev,
			"SCMI Voltage Domain %d already in use. Skipping: %s\n",
			dom, np->full_name);
		return -EINVAL;
	}

	rinfo->sregv[dom] = devm_kzalloc(&sdev->dev,
					 sizeof(struct scmi_regulator),
					 GFP_KERNEL);
	if (!rinfo->sregv[dom])
		return -ENOMEM;

	rinfo->sregv[dom]->id = dom;
	rinfo->sregv[dom]->sdev = sdev;
	rinfo->sregv[dom]->ph = ph;

	/* get hold of good nodes */
	of_node_get(np);
	rinfo->sregv[dom]->of_node = np;

	dev_dbg(&sdev->dev,
		"Found SCMI Regulator entry -- OF node [%d] -> %s\n",
		dom, np->full_name);

	return 0;
}

process_scmi_regulator_of_node函数做了下面几件事:

  1. 调用函数of_property_read_u32获取regulator节点下reg属性的数值,记录在dom中,
  2. 如果获取不到reg的值,或者reg的值超出host支持的数量,有或者reg的值重复,都会返回错误,
  3. 分配struct scmi_regulator的内存,并且记录在rinfo的sregv数组下标为dom的成员中,其中包括了struct regulator_desc的内存,
  4. 设置scmi_regulator结构体的成员,

1.3.4 scmi_regulator_common_init

scmi_regulator_common_init函数是真的regulator节点处理函数,需要通过scmi协议获取相关信息,方便后面注册regulator的:

c 复制代码
static int scmi_regulator_common_init(struct scmi_regulator *sreg)
{
	int ret;
	struct device *dev = &sreg->sdev->dev;
	const struct scmi_voltage_info *vinfo;

	vinfo = voltage_ops->info_get(sreg->ph, sreg->id);
	if (!vinfo) {
		dev_warn(dev, "Failure to get voltage domain %d\n",
			 sreg->id);
		return -ENODEV;
	}

	/*
	 * Regulator framework does not fully support negative voltages
	 * so we discard any voltage domain reported as supporting negative
	 * voltages: as a consequence each levels_uv entry is guaranteed to
	 * be non-negative from here on.
	 */
	if (vinfo->negative_volts_allowed) {
		dev_warn(dev, "Negative voltages NOT supported...skip %s\n",
			 sreg->of_node->full_name);
		return -EOPNOTSUPP;
	}

	sreg->desc.name = devm_kasprintf(dev, GFP_KERNEL, "%s", vinfo->name);
	if (!sreg->desc.name)
		return -ENOMEM;

	sreg->desc.id = sreg->id;
	sreg->desc.type = REGULATOR_VOLTAGE;
	sreg->desc.owner = THIS_MODULE;
	sreg->desc.of_match_full_name = true;
	sreg->desc.of_match = sreg->of_node->full_name;
	sreg->desc.regulators_node = "regulators";
	if (vinfo->segmented)
		ret = scmi_config_linear_regulator_mappings(sreg, vinfo);
	else
		ret = scmi_config_discrete_regulator_mappings(sreg, vinfo);
	if (ret)
		return ret;

	/*
	 * Using the scmi device here to have DT searched from Voltage
	 * protocol node down.
	 */
	sreg->conf.dev = dev;

	/* Store for later retrieval via rdev_get_drvdata() */
	sreg->conf.driver_data = sreg;

	return 0;
}

scmi_regulator_common_init做了以下几件事:

  1. 调用scmi协议函数voltage_ops->info_get获取对应regulator节点的信息,
  2. struct regulator_desc内存已经在上一个函数分配了,这里根据scmi获取到的信息设置struct regulator_desc的成员,
  3. 最后根据电压的类型,如果是线性电压,调用函数scmi_config_linear_regulator_mappings设置设置struct regulator_desc中支持电压的成员和ops方法集;如果是散列电压,则调用函数scmi_config_discrete_regulator_mappings设置struct regulator_desc中支持电压的成员和ops方法集,

1.3.5 devm_regulator_register

devm_regulator_register最关键还是通过调用函数regulator_register进行注册的:

c 复制代码
struct regulator_dev *
regulator_register(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;
	struct device *dev;
	int ret, i;

	if (cfg == NULL)
		return ERR_PTR(-EINVAL);
	if (cfg->ena_gpiod)
		dangling_cfg_gpiod = true;
	if (regulator_desc == NULL) {
		ret = -EINVAL;
		goto rinse;
	}

	dev = cfg->dev;
	WARN_ON(!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;
	}

	/* Only one of each should be implemented */
	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);

	/* If we're using selectors we must implement 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;
	}

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

	/*
	 * Duplicate the config so the driver could override it after
	 * parsing init data.
	 */
	config = kmemdup(cfg, sizeof(*cfg), GFP_KERNEL);
	if (config == NULL) {
		ret = -ENOMEM;
		goto clean;
	}

	init_data = regulator_of_get_init_data(dev, regulator_desc, config,
					       &rdev->dev.of_node);

	/*
	 * Sometimes not all resources are probed already so we need to take
	 * that into account. This happens most the time if the ena_gpiod comes
	 * from a gpio extender or something else.
	 */
	if (PTR_ERR(init_data) == -EPROBE_DEFER) {
		ret = -EPROBE_DEFER;
		goto clean;
	}

	/*
	 * We need to keep track of any GPIO descriptor coming from the
	 * device tree until we have handled it over to the core. If the
	 * config that was passed in to this function DOES NOT contain
	 * a descriptor, and the config after this call DOES contain
	 * a descriptor, we definitely got one from parsing the device
	 * tree.
	 */
	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, &regulator_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);

	/* preform any regulator specific init */
	if (init_data && init_data->regulator_init) {
		ret = init_data->regulator_init(rdev->reg_data);
		if (ret < 0)
			goto clean;
	}

	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 clean;
		}
		/* The regulator core took over the GPIO descriptor */
		dangling_cfg_gpiod = false;
		dangling_of_gpiod = false;
	}

	/* register with sysfs */
	rdev->dev.class = &regulator_class;
	rdev->dev.parent = dev;
	dev_set_name(&rdev->dev, "regulator.%lu",
		    (unsigned long) atomic_inc_return(&regulator_no));
	dev_set_drvdata(&rdev->dev, rdev);

	/* set regulator constraints */
	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 (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;

	ret = set_machine_constraints(rdev);
	if (ret == -EPROBE_DEFER) {
		/* Regulator might be in bypass mode and so needs its supply
		 * to set the constraints
		 */
		/* FIXME: this currently triggers a chicken-and-egg problem
		 * when creating -SUPPLY symlink in sysfs to a regulator
		 * that is just being created
		 */
		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;

	ret = regulator_init_coupling(rdev);
	if (ret < 0)
		goto wash;

	/* add consumers devices */
	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;

	rdev_init_debugfs(rdev);

	/* try to resolve regulators coupling since a new one was registered */
	mutex_lock(&regulator_list_mutex);
	regulator_resolve_coupling(rdev);
	mutex_unlock(&regulator_list_mutex);

	/* try to resolve regulators supply since a new one was registered */
	class_for_each_device(&regulator_class, NULL, NULL,
			      regulator_register_resolve_supply);
	kfree(config);
	return rdev;
}

二、consumer驱动

2.1 设备树

我们先找一个消费者的设备树看看:

c 复制代码
&sdmmc {
	bus-width = <4>;
	cap-sd-highspeed;
	cd-gpios = <&gpio0 RK_PA7 GPIO_ACTIVE_LOW>;
	disable-wp;
	max-frequency = <150000000>;
	pinctrl-names = "default";
	pinctrl-0 = <&sdmmc_clk &sdmmc_cmd &sdmmc_bus4>;
	sd-uhs-sdr104;
	vmmc-supply = <&vcc3v0_sd>;
	vqmmc-supply = <&vcc_sdio>;
	status = "okay";
};

关键是vmmc-supply = <&vcc3v0_sd>;和vqmmc-supply = <&vcc_sdio>;,驱动会通过api获取到对应的regulator设备。

我们也可以看看cpu的设备树:

c 复制代码
&cpu0 {
    cpu-supply = <&vdd_arm>;
};
&cpu1 {
    cpu-supply = <&vdd_arm>;
};
&cpu2 {
    cpu-supply = <&vdd_arm>;
};
&cpu3 {
    cpu-supply = <&vdd_arm>;
};

cpu-supply = <&vdd_arm>;会被regulator_get函数获取到对应的regulator。

2.2 api函数

2.2.1 获取regulator接口

c 复制代码
struct regulator *regulator_get(struct device *dev, const char *id)
struct regulator *devm_regulator_get(struct device *dev, const char *id)
struct regulator *regulator_get_exclusive(struct device *dev, const char *id)
struct regulator *regulator_get_optional(struct device *dev, const char *id)

regulator_get为普通的regulator句柄获取接口,内存的资源在申请时分配。

devm_regulator_get是regulator封装了一层的devres机制接口,便于管理内存资源。

regulator_get_exclusive,重点在最后一个单词"exclusive"​,这里的专有、专用对于consumer来说是有先后顺序的,第一个获取regulator的consumer有了优先使用权,后续consumer再想获取是不可能了。

regulator_get_optional,可选,用于获取regulator。

上面这些函数其实都是调用_regulator_get函数而已,只有第三个参数有所不同,我们先看看第三个参数:

c 复制代码
enum regulator_get_type {
	NORMAL_GET,		//普通获取
	EXCLUSIVE_GET,	//独占获取
	OPTIONAL_GET,	//可选获取
	MAX_GET_TYPE	//边界标记
};

_regulator_get函数我们后面再分析。

2.2.2 释放regulator接口

驱动注销或异常时,需要释放已获取的regulator,因为获取接口申请了不少内存资源。

c 复制代码
static void devm_regulator_release(struct device *dev, void *res)
void devm_regulator_put(struct regulator *regulator)

2.2.3 regulator控制和状态接口

c 复制代码
int regulator_enable(struct regulator *regulator) 	//启用稳压器
int regulator_disable(struct regulator *regulator)	//禁用稳压器
int regulator_force_disable(struct regulator *regulator)	//强制禁用稳压器,无视引用计数
int regulator_disable_deferred(struct regulator *regulator, int ms)//延迟禁用稳压器
int regulator_is_enabled(struct regulator *regulator)//查询稳压器当前硬件启用状态

上述接口的作用是控制电源的打开、关闭,以及查询当前状态。下面的接口主要用于获取电源电压、电流,配置电压、电流,同时获取电压列表、压差步长、电流限制、模式等。

c 复制代码
int regulator_count_voltages(struct regulator *regulator)
int regulator_list_voltage(struct regulator *regulator, unsigned selector)
int regulator_is_supported_voltage(struct regulator *regulator,
				   int min_uV, int max_uV)
int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV)
int regulator_suspend_enable(struct regulator_dev *rdev,
				    suspend_state_t state)
int regulator_suspend_disable(struct regulator_dev *rdev,
				     suspend_state_t state)
int regulator_set_suspend_voltage(struct regulator *regulator, int min_uV,
				  int max_uV, suspend_state_t state)
int regulator_get_voltage(struct regulator *regulator)
int regulator_set_mode(struct regulator *regulator, unsigned int mode)
unsigned int regulator_get_mode(struct regulator *regulator)
  • regulator_count_voltages:查询该稳压器支持的电压档位总数
  • regulator_list_voltage:查询指定选择器索引 (selector) 对应的电压值
  • regulator_is_supported_voltage:检查稳压器是否能提供指定范围内的某个电压
  • regulator_set_voltage:请求将输出电压设置在 [min_uV, max_uV] 微伏范围内
  • regulator_suspend_enable:启用指定休眠状态 (state) 下的稳压器输出
  • regulator_suspend_disable:禁用指定休眠状态下的稳压器输出
  • regulator_set_suspend_voltage:设置稳压器在指定休眠状态下的输出电压
  • regulator_get_voltage:查询稳压器当前实际配置的输出电压
  • regulator_set_mode:设置稳压器工作模式
  • regulator_get_mode:获取稳压器当前工作模式

2.2.4 多regulator控制接口

还有一类特殊接口,需要一次控制多个regulator,如MMC需要1.9 V和1.0 V两路电源一起供电才能正常工作。打开两路电源有两种方法,即通过regulator_get获取两路电源后控制,或者通过regulator bulk接口控制电源。

这里有个重要的结构体regulator_bulk_data,明白该结构体及regulator_bulk_get实现就可以很轻易地明白bulk接口的实现原理。regulator_bulk_data本质对应一个电源(regulator),supply为获取regulator的参数。

c 复制代码
	struct regulator_bulk_data {
		const char *supply;
		struct regulator *consumer;
	
		/* private: Internal use */
		int ret;
	};

consumer通过regulator_bulk_get一次获取多个regulator,通过regulator_bulk_get实现可知,bulk相关接口通过struct regulator_bulk_data数组控制了多个regulator。目前bulk接口只支持获取、释放、使能、去使能,不支持配置电压:

c 复制代码
int regulator_bulk_get(struct device *dev, int num_consumers,
		       struct regulator_bulk_data *consumers)
int regulator_bulk_enable(int num_consumers,
			  struct regulator_bulk_data *consumers)
int regulator_bulk_disable(int num_consumers,
			   struct regulator_bulk_data *consumers)
int regulator_bulk_force_disable(int num_consumers,
			   struct regulator_bulk_data *consumers)
void regulator_bulk_free(int num_consumers,
			 struct regulator_bulk_data *consumers)
相关推荐
安科士andxe4 小时前
深入解析|安科士1.25G CWDM SFP光模块核心技术,破解中长距离传输痛点
服务器·网络·5g
小白同学_C7 小时前
Lab4-Lab: traps && MIT6.1810操作系统工程【持续更新】 _
linux·c/c++·操作系统os
今天只学一颗糖7 小时前
1、《深入理解计算机系统》--计算机系统介绍
linux·笔记·学习·系统架构
2601_949146537 小时前
Shell语音通知接口使用指南:运维自动化中的语音告警集成方案
运维·自动化
儒雅的晴天7 小时前
大模型幻觉问题
运维·服务器
Gofarlic_OMS8 小时前
科学计算领域MATLAB许可证管理工具对比推荐
运维·开发语言·算法·matlab·自动化
通信大师8 小时前
深度解析PCC策略计费控制:核心网产品与应用价值
运维·服务器·网络·5g
dixiuapp8 小时前
智能工单系统如何选,实现自动化与预测性维护
运维·自动化
不做无法实现的梦~8 小时前
ros2实现路径规划---nav2部分
linux·stm32·嵌入式硬件·机器人·自动驾驶
Elastic 中国社区官方博客8 小时前
如何防御你的 RAG 系统免受上下文投毒攻击
大数据·运维·人工智能·elasticsearch·搜索引擎·ai·全文检索