Linux regulator子系统分析3

Linux regulator子系统分析3(基于Linux6.6)---相关接口介绍


一、regulator device的注册与注销

针对regulator device的注册与注销函数主要涉及regulator_register、regulator_unregister这两个函数。而针对regulator_register函数,其实现哪些功能呢?

  1. 若该regulator_dev支持使用gpio控制使能与否,则完成下图标号3的gpio相关配置,并将该regulator_dev 使能控制相关的gpio参数注册到下图标号7中的链表regulator_env_gpio_list,通过函数regulator_ena_gpio_request实现;
  2. 将该regulator dev的所有使用者相关的信息(struct regulator_map类型变量),注册到下图标号5中的链表regulator_map_list;
  3. 确定该regulator_dev的约束信息(下图标号4 struct regulator_constraints类型的变量配置,通过函数set_machine_constraints实现);
  4. 完成该regulator_dev的注册,即将该regulator_dev注册到下图标号6中所示的regulator_list上;

除了上述四个主要的内容外,还涉及如下内容:

  1. 对regulator dev相关的参数进行合法性检测,如get_voltage_sel、list_voltage这两个接口是否只设置一个等;
  2. 为该regulator_dev创建struct device类型的变量,并注册到linux设备驱动模型子系统中,并完成与regulator_class的关联;
  3. 为该regulator_dev创建设备属性文件(在sysfs文件系统下创建属性文件,以便应用程序通过设备属性文件即可查看该regulator_dev相关的配置信息,如最小电压、最大电压、操作模式、使能状态、bypass、suspend相关state等),通过函数add_regulator_attributes;
  4. 若该regulator dev由别的regulator dev提供电压/电流输入,则需要使能其输出,通过函数regulator_enable实现;

以上即为regulator_register实现的主要功能。而针对regulator_unregister则主要实现相反的操作。

drivers/regulator/core.c

/**
 * regulator_register - register regulator
 * @dev: the device that drive the regulator
 * @regulator_desc: regulator to register
 * @cfg: runtime configuration for regulator
 *
 * Called by regulator drivers to register a regulator.
 * Returns a valid pointer to struct regulator_dev on success
 * or an ERR_PTR() on error.
 */
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;

	if (cfg == NULL)
		return ERR_PTR(-EINVAL);
	if (cfg->ena_gpiod)
		dangling_cfg_gpiod = true;
	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;
	}

	/* 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);
	dev_set_drvdata(&rdev->dev, rdev);
	rdev->dev.class = &regulator_class;
	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);

	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;

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

	/* 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 ((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;
	}

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

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

	ret = set_machine_constraints(rdev);
	if (ret == -EPROBE_DEFER && !resolved_early) {
		/* 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;

unset_supplies:
	mutex_lock(&regulator_list_mutex);
	unset_regulator_supplies(rdev);
	regulator_remove_coupling(rdev);
	mutex_unlock(&regulator_list_mutex);
wash:
	regulator_put(rdev->supply);
	kfree(rdev->coupling_desc.coupled_rdevs);
	mutex_lock(&regulator_list_mutex);
	regulator_ena_gpio_free(rdev);
	mutex_unlock(&regulator_list_mutex);
clean:
	if (dangling_of_gpiod)
		gpiod_put(config->ena_gpiod);
	kfree(config);
	put_device(&rdev->dev);
rinse:
	if (dangling_cfg_gpiod)
		gpiod_put(cfg->ena_gpiod);
	return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(regulator_register);

/**
 * regulator_unregister - unregister regulator
 * @rdev: regulator to unregister
 *
 * Called by regulator drivers to unregister a regulator.
 */
void regulator_unregister(struct regulator_dev *rdev)
{
	if (rdev == NULL)
		return;

	if (rdev->supply) {
		while (rdev->use_count--)
			regulator_disable(rdev->supply);
		regulator_put(rdev->supply);
	}

	flush_work(&rdev->disable_work.work);

	mutex_lock(&regulator_list_mutex);

	WARN_ON(rdev->open_count);
	regulator_remove_coupling(rdev);
	unset_regulator_supplies(rdev);
	list_del(&rdev->list);
	regulator_ena_gpio_free(rdev);
	device_unregister(&rdev->dev);

	mutex_unlock(&regulator_list_mutex);
}
EXPORT_SYMBOL_GPL(regulator_unregister);

二、regulator的注册与注销(regulator device的使用者)

针对regulator_dev的使用者,则主要涉及接口regulator_get、regulator_put这两个函数。而针对regulator_get接口,其主要用于创建struct regulator类型的变量,并完成与regulator_dev的关联,建立如下图的关联关系,具体说明如下:

  1. 根据dev_name、supply name,在regulator_map_list上进行查找与匹配,若查找到,即获取到对应的regulator_dev;
  2. 调用create_regulator接口创建一个regulator,并加入到regulator_dev的consumer_list链表中;

三、regulator子系统提供的接口

调节器操作接口

在调节器被成功创建并注册后,消费者可以使用各种操作接口来控制调节器的行为。这些操作通常包括启用、禁用调节器,设置和获取电压、电流,设置调节器的工作模式等。

3.1、电压与电流的设置与获取

  • regulator_set_voltage():设置调节器输出的电压。该函数允许消费者根据需要设置调节器的电压输出。

  • regulator_get_voltage():获取调节器当前的输出电压。

  • regulator_set_current_limit():设置调节器的电流上限,以防止调节器因过载而损坏。

  • regulator_get_current_limit():获取调节器的电流上限。

int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV);
int regulator_get_voltage(struct regulator *regulator);
int regulator_set_current_limit(struct regulator *regulator, int min_uA, int max_uA);
int regulator_get_current_limit(struct regulator *regulator);

3.2、电源管理操作

  • regulator_enable():启用调节器,使其开始提供电源。

  • regulator_disable():禁用调节器,停止其电源输出。

  • regulator_is_enabled():检查调节器是否处于启用状态。

  • regulator_force_disable():强制禁用调节器,跳过可能的依赖关系。

int regulator_enable(struct regulator *regulator);
int regulator_disable(struct regulator *regulator);
int regulator_is_enabled(struct regulator *regulator);
void regulator_force_disable(struct regulator *regulator);

3.3、调节器工作模式与电压列表

  • regulator_get_mode():获取调节器的当前工作模式(如:自动模式、强制模式等)。

  • regulator_set_mode():设置调节器的工作模式。

  • regulator_list_voltage():列出调节器支持的所有电压值。

int regulator_get_mode(struct regulator *regulator);
int regulator_set_mode(struct regulator *regulator, unsigned int mode);
int regulator_list_voltage(struct regulator *regulator, unsigned int selector);

3.4、调节器挂起与恢复

  • regulator_suspend_prepare():在系统挂起之前准备调节器,确保电源管理的平稳过渡。

  • regulator_suspend_finish():在系统恢复后完成调节器的恢复工作。

int regulator_suspend_prepare(struct regulator *regulator);
int regulator_suspend_finish(struct regulator *regulator);

3.5、批量获取与管理多个调节器

  • regulator_bulk_get():同时获取多个调节器,以便同时管理多个电源轨。
int regulator_bulk_get(struct device *dev, unsigned n, struct regulator_bulk_data *bulk);

使用案例与操作流程

外设驱动通过 regulator_get() 获取 struct regulator 句柄后,可以调用上述接口进行电压、电流设置、调节器启用/禁用等操作。操作流程通常如下:

  1. 获取调节器:外设驱动调用 regulator_get() 获取所需调节器的句柄。

  2. 设置电压和电流:通过 regulator_set_voltage()regulator_set_current_limit() 来配置调节器的电压和电流。

  3. 启用调节器:调用 regulator_enable() 启用调节器,使其开始为设备供电。

  4. 禁用调节器:当外设不再需要电源时,调用 regulator_disable() 来禁用调节器。

  5. 释放资源:当外设不再需要调节器时,调用 regulator_put() 释放对调节器的引用,触发调节器的销毁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值