Linux字符设备驱动 -- regulator子系统

文章目录

  • 环境
  • regulator子系统简介:
  • Regulator设备的注册
  • Consumer设备的注册

环境

linux 4.9
armv8-A

regulator子系统简介:

关于regulator子系统,可以看下这这些博客:

  • Linux驱动之Regulator子系统
  • Linux 内核之电源篇(加载流程)

regulator,翻译就是调节器。一些可以输出电流电压的设备可以使用该子系统。举个例子,一个PMIC有多路输出,每一路输出都可以给很多外设、芯片供电。那么每一路输出,我都可以认为他是一个regulator。

regulator子系统还是有一个思想就是consumer(消费者),继续刚才那个例子,PMIC的某一路输出,要给DDR供电,那么这个DDR设备就是这一路regulator的consumer,可以注册为一个consumer设备。

当然,这是真正有物理设备的consumer。有的时候为了控制regulator,不需要通过一个真正的物理设备注册的consumer,可以凭空捏造一个consumer,就是virtual consumer。后面分析的时候可以看到,对consumer的开关/电压/电流操作实际上会转化为对regulator的操作。

那为啥不直接控制regulator呢?我也不知道,看上去像是regulator子系统没有提供直接对regulator控制的API,还是要以来consumer提供的API来控制regulator,所以还是要搞个虚拟consumer。不过这一段我的理解可能有问题。

回顾笔记,分析都在注释中。

Regulator设备的注册

对于一个多路输出的PMIC,每一路都可以注册为一个regulator。把每一路注册为一个regulator使用的子系统接口API为:

rdev = devm_regulator_register(&pdev->dev, regulator, &regulator_config);

需要传入参数:

  1. struct device *dev,这里传入&pdev->dev,所有regulator总要有一个父设备的,在设备树中我们把所有regulator节点注册为一个regulators节点的子节点。然后这个regulators节点就是这里的platform_device,但是不是在系统启动时注册的,是我们上一节通过MFD子系统注册的。
  2. const struct regulator_desc *regulator_desc,这是regulator的静态描述信息。这些信息是用来描述regulator设备本身的
  3. const struct regulator_config *config,这是regulator的动态描述信息。这些信息是用来填充一些结构关系的。

第二个参数结构如下:

struct regulator_desc {
	const char *name;								// g, 该regulator名称
	const char *supply_name;						// g, 
	const char *of_match;							// g, 用于与设备树中的regulator匹配,会在初测过程中与regulaotr的设备树节点名字匹配,也就是devcice_node->name
	const char *regulators_node;					// g, 一般会在设备树中把所有regulator的节点都挂在一个regulators节点下,此域表示regulators节点的名字。在注册regulator的时候需要用到
	int (*of_parse_cb)(struct device_node *,		// g, 自己提供,如果of_match存在且匹配,可以用这个函数来解析一些设备树中regulator节点中自己定义的属性,然后做一些自己想做的操作
			    const struct regulator_desc *,
			    struct regulator_config *);
	int id;											// g, regulator id
	unsigned int continuous_voltage_range:1;
	unsigned n_voltages;							// g, 步长数,可控制的电压范围内总共有多少步
	const struct regulator_ops *ops;				// g, 操作集,该regulator专有的操作集,后面对consumer的控制实际上会调用到这里
	int irq;
	enum regulator_type type;
	struct module *owner;

	unsigned int min_uV;							// g, 最小电压,控制regulator时不能小与该电压
	unsigned int uV_step;							// g, 步长,看过PMIC的芯片手册就知道,某一路的输出电压是按步长来的,bit +1 则输出电压+step.但是对于变步长的regulator来说不需要这个
	unsigned int linear_min_sel;
	int fixed_uV;
	unsigned int ramp_delay;
	int min_dropout_uV;

	// g, 有些regulator的电压/电流控制是变步长的,就比如说在[a,b)这一段,vsel_reg一个bit的步长为step1,在[b,c]这一段1个bit步长为step2
	// g, 此时就需要下面这个linear_ranges域,这个域是个数组,每个数组可以表示"[a,b)这一段电压范围使用步长step1, 步的范围为[c,d]"中的a, c, d, step1
	// g, 则b可由公式 b = a + step1 * (d -c)计算得出
	const struct regulator_linear_range *linear_ranges;	
	int n_linear_ranges;

	const unsigned int *volt_table;

	unsigned int vsel_reg;							// g, 电压控制寄存器,控制该路regulator输出电压的寄存器
	unsigned int vsel_mask;							// g, 可能这个电压控制寄存器的某几位控制输出电压,使用该掩码控制这几位特定的bit
	unsigned int csel_reg;							// g, 电流控制
	unsigned int csel_mask;							// g, 同上
	unsigned int apply_reg;
	unsigned int apply_bit;
	unsigned int enable_reg;						// g, regulator使能寄存器地址
	unsigned int enable_mask;						// g, 同上
	unsigned int enable_val;						
	unsigned int disable_val;
	bool enable_is_inverted;
	unsigned int bypass_reg;
	unsigned int bypass_mask;
	unsigned int bypass_val_on;
	unsigned int bypass_val_off;
	unsigned int active_discharge_on;
	unsigned int active_discharge_off;
	unsigned int active_discharge_mask;
	unsigned int active_discharge_reg;

	unsigned int enable_time;

	unsigned int off_on_delay;

	unsigned int (*of_map_mode)(unsigned int mode);
};

第三个参数结构如下:

struct regulator_config {
	struct device *dev;								// g, 一般你的regulators节点挂在哪个设备下,这里就要使用哪个设备。
	const struct regulator_init_data *init_data;	// g, 一些初始化数据,如果创建实例的时候初始化了这个域,并且无法从设备树中解析到对应的设备,就会使用我们初始化的域。否则,会使用设备树的信息填充该域,就算你初始化了,也会被覆盖。
	void *driver_data;								// g, 私有数据
	struct device_node *of_node;					// g, 设备节点
	struct regmap *regmap;							// g, 使用的regmap

	bool ena_gpio_initialized;
	int ena_gpio;
	unsigned int ena_gpio_invert:1;
	unsigned int ena_gpio_flags;
};

其中比较重要的是第二个参数,描述了该regulator本身的一些物理特性,具体配置成什么样要根据你使用的regulator来设置。举个例子:


static const struct regulator_desc pmic_regulators_desc [] = {
			[0] = {
				...
			},
			[1] = {
			.name = "dcdcb",								// g, 这一路regulator的名字是"dcdcb"
			.supply_name	= 	"vinb",						
			.of_match	= 		of_match_ptr("dcdcb"),		// g, .of_match="dcdcb",如果有需要的话,可以使用这个域来与设备树节点匹配,当然需要设备树对应的节点名字为"dcdcb"
			.regulators_node = 	of_match_ptr("regulators"),// g, 这个regulator挂在regulators节点下,而且这个regulators节点在设备树中就叫"regulators"
			.type		= 		REGULATOR_VOLTAGE,
			.id		= 			1,
			.n_voltages	= 		(2550 - 1000) / 50 + 1, // g, 根据dataset,范围为1v~2.55v
			.owner		= 		THIS_MODULE,
			.min_uV		= 		1000 * 1000,			// g,最小电压为1000mv,也就是1v
			.uV_step	= 		50 * 1000(fix),			// g, 步长为50mv, 这是个定步长regulator
			.vsel_reg	= 		0x13,					// g, 控制该regulator电压的寄存器地址为0x13
			.vsel_mask	= 		0x1f,					// g, 寄存器0x13的bit 0~4 控制 DCDC-B的输出voltage
			.enable_reg	= 		0x10,					// g, 开/关寄存器地址
			.enable_mask	= 	BIT(1), 				// g, 寄存器0x10的bit 1控制DCDC-B的on-off
			.ops		= 		&自定义,				// g,自定义,但是很重要。
		}
};
...
...
static int regulator_probe(struct platform_device *pdev)
{
	struct regulator_config pmic_regulator_config= {
		.dev = pdev->dev.parent,		// g, pmu节点,因为在我的设备树下regulators节点是挂在pmu节点下面的
		.regmap = 之前注册的regmap,
		.driver_data = 看你自己需要什么,
	};
	...
	...
	struct regulator_dev *rdev = devm_regulator_register(&pdev->dev, pmic_regulators_desc, &pmic_regulator_config);
	...
}

注册过程是什么情况呢?具体得看devm_regulator_register()的实现过程:

drivers/regulator/devres.c:
struct regulator_dev *devm_regulator_register(struct device *dev,
				  const struct regulator_desc *regulator_desc,
				  const struct regulator_config *config)
{
	struct regulator_dev **ptr, *rdev;

	ptr = devres_alloc(devm_rdev_release, sizeof(*ptr),
			   GFP_KERNEL);
	if (!ptr)
		return ERR_PTR(-ENOMEM);

	// g, 这个函数有说法的
	// g, in
	rdev = regulator_register(regulator_desc, config);
	if (!IS_ERR(rdev)) {
		*ptr = rdev;
		// g, 会为dev->devres_head添加dr->node->entry
		// g, 也就是说新注册的regulator_dev是传入的dev的一个devres
		devres_add(dev, ptr);		
	} else {
		devres_free(ptr);
	}

	return rdev;
}

因为是devm_开头的函数,所以肯定是把资源挂在dev->devres_head下,关于devm_系列的函数,后续单独写一篇笔记分析。除此之外,这个函数就单纯的调用了regulator_register(),会注册一个regulator_dev,并返回:

struct regulator_dev *
regulator_register(const struct regulator_desc *regulator_desc,
		   const struct regulator_config *cfg)
{
	const struct regulation_constraints *constraints = NULL;
	const struct regulator_init_data *init_data;
	struct regulator_config *config = NULL;
	static atomic_t regulator_no = ATOMIC_INIT(-1);
	struct regulator_dev *rdev;
	struct device *dev;
	int ret, i;

	if (regulator_desc == NULL || cfg == NULL)
		return ERR_PTR(-EINVAL);

	dev = cfg->dev;
	WARN_ON(!dev);

	if (regulator_desc->name == NULL || regulator_desc->ops == NULL)
		return ERR_PTR(-EINVAL);

	if (regulator_desc->type != REGULATOR_VOLTAGE &&
	    regulator_desc->type != REGULATOR_CURRENT)
		return ERR_PTR(-EINVAL);

	/* 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) {
		return ERR_PTR(-EINVAL);
	}
	if (regulator_desc->ops->set_voltage_sel &&
	    !regulator_desc->ops->list_voltage) {
		return ERR_PTR(-EINVAL);
	}

	rdev = kzalloc(sizeof(struct regulator_dev), GFP_KERNEL);
	if (rdev == NULL)
		return ERR_PTR(-ENOMEM);

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

	// g, 这个init_data挺关键的,一般会放在config中,但是我们的config没有定义init_data
	// g, 此处传入的dev为cfg->dev,也就是config->dev,也就是pdev("regulators")->dev.parent("pmu")
	// g, 该函数会解析(pmu-->regulators-->子regulator)中的很多属性,赋值给init_data。同时会把子regulator结点作为regulator_dev->dev.of_node
	// g, in
	init_data = regulator_of_get_init_data(dev, regulator_desc, config,
					       &rdev->dev.of_node);
	if (!init_data) {	// g, 如果解析设备树失败,才会使用config->init_data,所以设备树优先级更高
		init_data = config->init_data;
		rdev->dev.of_node = of_node_get(config->of_node);
	}

	mutex_init(&rdev->mutex);
	rdev->reg_data = config->driver_data;
	rdev->owner = regulator_desc->owner;
	rdev->desc = regulator_desc;		// g,把desc表放到rdev->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);
#if defined(CONFIG_AW_AXP)
	INIT_LIST_HEAD(&rdev->axp_enable_list);
	list_add(&rdev->list, &axp_regulator_list);
#endif
	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_gpio || config->ena_gpio_initialized) &&
	    gpio_is_valid(config->ena_gpio)) {
		mutex_lock(&regulator_list_mutex);
		ret = regulator_ena_gpio_request(rdev, config);
		mutex_unlock(&regulator_list_mutex);
		if (ret != 0) {
			rdev_err(rdev, "Failed to request enable GPIO%d: %d\n",
				 config->ena_gpio, ret);
			goto clean;
		}
	}

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

	/* set regulator constraints */
	if (init_data)
		constraints = &init_data->constraints;

	if (init_data && init_data->supply_regulator)
		rdev->supply_name = init_data->supply_regulator;
	else if (regulator_desc->supply_name)	// g, 这里会进行一个supply_name命名,如"vina", "vinb"
		rdev->supply_name = regulator_desc->supply_name;

	/*
	 * Attempt to resolve the regulator supply, if specified,
	 * but don't return an error if we fail because we will try
	 * to resolve it again later as more regulators are added.
	 */
	if (regulator_resolve_supply(rdev))
		rdev_dbg(rdev, "unable to resolve supply\n");

	ret = set_machine_constraints(rdev, constraints);
	if (ret < 0)
		goto wash;

	/* add consumers devices */
	if (init_data) {
		mutex_lock(&regulator_list_mutex);
		for (i = 0; i < init_data->num_consumer_supplies; i++) {
			ret = set_consumer_device_supply(rdev,				// g, 向全局链表regulator_map_list中添加新的节点,新结点表示新创建的regulator_dev
				init_data->consumer_supplies[i].dev_name,
				init_data->consumer_supplies[i].supply);
			if (ret < 0) {
				mutex_unlock(&regulator_list_mutex);
				dev_err(dev, "Failed to set supply %s\n",
					init_data->consumer_supplies[i].supply);
				goto unset_supplies;
			}
		}
		mutex_unlock(&regulator_list_mutex);
	}

	if (!rdev->desc->ops->get_voltage &&
	    !rdev->desc->ops->list_voltage &&
	    !rdev->desc->fixed_uV)
		rdev->is_switch = true;

	dev_set_drvdata(&rdev->dev, rdev);
	ret = device_register(&rdev->dev);
	if (ret != 0) {
		put_device(&rdev->dev);
		goto unset_supplies;
	}

	// g, 使用debugfs_create_dir在debugfs(/sys/kernel/debug/regulator)下创建调试节点
	// g, 命名为:"rdev->dev.parent->name"-"rdev->name",
	// g, rdev->dev.parent是传入的cfg->dev,我们设置的cfg->dev就是PMIC的dev
	// g, 所以最终的debug节点为:/sys/kernel/debug/regulator/PMIC名-aldo1
	rdev_init_debugfs(rdev);

	/* 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);
	mutex_unlock(&regulator_list_mutex);
wash:
	kfree(rdev->constraints);
	mutex_lock(&regulator_list_mutex);
	regulator_ena_gpio_free(rdev);
	mutex_unlock(&regulator_list_mutex);
clean:
	kfree(rdev);
	kfree(config);
	return ERR_PTR(ret);
}

注册设备,已经相应的调试节点的注册,都在注释中了。其中有个函数需要深入分析一下,这个函数决定了设备树应该怎么写,虽然一般documentation/devicetree/下会告诉你怎么写,但是还是想去看一下。这个函数是regulator_of_get_init_data(),该函数会解析设备树信息填充一个结构体变量struct regulator_init_data init_data,此变量会用来初始化一些regulator_dev的域:

struct regulator_init_data *regulator_of_get_init_data(struct device *dev,
					    const struct regulator_desc *desc,
					    struct regulator_config *config,
					    struct device_node **node)
{
	struct device_node *search, *child;
	struct regulator_init_data *init_data = NULL;
	const char *name;

	if (!dev->of_node || !desc->of_match)
		return NULL;

	// g, desc->regulators_node = "regulators", 自己定义在静态信息表中,可以认为是当前regulator挂在哪个节点下
	// g, 一般PMIC驱动,每一路输出(regulator)都会挂在一个节点下,该节点可以认为是regulator的集合(在设备树中被命名为"regulators").
	if (desc->regulators_node)		
		search = of_get_child_by_name(dev->of_node,	// g, 从dev->of_node遍历寻找"regulators节点",这个节点是PMIC节点,regulator集合节点就挂在PMIC节点下
					      desc->regulators_node);
	else
		search = dev->of_node;

	if (!search) {
		dev_dbg(dev, "Failed to find regulator container node '%s'\n",
			desc->regulators_node);
		return NULL;
	}

	// g, 遍历整个"regulators"结点(search),找子结点,在设备树中就是dcdca,dcdcb这些
	for_each_available_child_of_node(search, child) {
		name = of_get_property(child, "regulator-compatible", NULL); // g, 该节点无该属性
		if (!name)
			name = child->name;	// g, name = child->name,也就是"dcdca", "dcdcb"

		// g, 如果child->name != of_match,则continue。
		// g, 806都是相等的,设备树与desc静态信息的fo_match是一致的
		if (strcmp(desc->of_match, name))	
			continue;

		// g, dev是"pmu",child是regulator小结点(pmu-->regulators-->子regulator)
		// g, 该函数会分配一个struct regulator_init_data, 解析很多child中的属性填充init_data 
		// g, 但是我们的设备树中的regulator中有几个自定义的属性,这里无法解析,需要在外面解析
		// g, in
		init_data = of_get_regulator_init_data(dev, child, desc);
		if (!init_data) {
			dev_err(dev,
				"failed to parse DT for regulator %s\n",
				child->name);
			break;
		}

		// g, 如果在静态信息表有自定义的of_parse_cb()函数,则需要在parse设备树之后调用回调
		// g, 比如说,我这个regulaotr下有些单独定义的属性,希望解析这些属性做一些对应的操作,就可以自己初始化一个of_parse_cb()用来解析.
		if (desc->of_parse_cb) {
			if (desc->of_parse_cb(child, desc, config)) {
				dev_err(dev,
					"driver callback failed to parse DT for regulator %s\n",
					child->name);
				init_data = NULL;
				break;
			}
		}

		of_node_get(child);	// g, 增加引用计数

		// g, 找到的子regulator结点(pmu-->regulators-->子regulator)会作为rdev(regulator_dev)->dev.of_node
		*node = child;		
		break;
	}

	of_node_put(search);	// g, 减少引用计数

	return init_data;
}

这样就为regulator注册好了regulator_dev,接下来需要注册consumer。

Consumer设备的注册

对于设备树中的virtual-consumer设备,可以使用regulator子系统提供的probe函数,匹配方法为:

drivers/regulator/virtual.c:
static struct platform_driver regulator_virtual_consumer_driver = {
	.probe		= regulator_virtual_probe,
	.remove		= regulator_virtual_remove,
	.driver		= {
		.name		= "reg-virt-consumer",
	},
};

可以看到,只能通过driver->name与platform_dev->name匹配这种方式进行匹配。不过在MFD设备注册的时候我们已经把virtual_consumer对应的cell所注册而成的plat_form_dev->name设置了"reg-virt-consumer"。

所以最终是使用regulator子系统提供的这个virtual-consumer驱动的。probe过程为regulator_virtual_probe():

drivers/regulator/virtual.c:
static int regulator_virtual_probe(struct platform_device *pdev)
{
	char *reg_id = dev_get_platdata(&pdev->dev);	// g, 拿到dev->platform_data,也就是AXP806_DCDC1_NAME("dcdc1")这些
	struct virtual_consumer_data *drvdata;
	int ret;

	drvdata = devm_kzalloc(&pdev->dev, sizeof(struct virtual_consumer_data),
			       GFP_KERNEL);
	if (drvdata == NULL)
		return -ENOMEM;

	mutex_init(&drvdata->lock);

	// g, 该函数的查找方式并不是通过reg_id和regulator_dev中的什么属性进行匹配
	// g, 而是将"reg_id"拚上"-supply"变成"reg_id-supply",然后调用of_parse_phandle()函数进行搜索device_node
	// g, 该函数会根据传入的结点,找到该结点中包含的引用属性"reg_id-supply"所指向的结点,
	// g, 比如说该结点包含一个引用属性"dcdc1-supply",设备树中的完整格式是:dcdc1-supply = <®_dcdc1>;
	// g, 最终找到的device_node就是reg_dcdc1对应的devcie_node, 这是一种隐式phandle(就是被引用的结点)
	// g, 再使用container_of找到node对应的regulator_dev

	// g, 下面这个函数,通过consumer_dev和reg_id,找到对应的regulator_dev,并且新建立一个struct regulator,挂在regulaot_dev->consumer_list链表下
	// g, 找节点的方式比较特殊,需要设备树的配合,也就是说设备树需要按照规范来写
	// g, in
	drvdata->regulator = devm_regulator_get(&pdev->dev, reg_id);
	if (IS_ERR(drvdata->regulator)) {
		ret = PTR_ERR(drvdata->regulator);
		dev_err(&pdev->dev, "Failed to obtain supply '%s': %d\n",
			reg_id, ret);
		return ret;
	}

	// g, 创建调试结点
	// g, 每个kobject,在sysfs中都会对应一个目录
	ret = sysfs_create_group(&pdev->dev.kobj,
				 &regulator_virtual_attr_group);
	if (ret != 0) {
		dev_err(&pdev->dev,
			"Failed to create attribute group: %d\n", ret);
		return ret;
	}

	// 这些regulator_xx等等函数,最终都会调用到drvdata->regulator->desc->ops->xxx,也就是为regulator_dev注册的ops函数
	drvdata->mode = regulator_get_mode(drvdata->regulator);

	platform_set_drvdata(pdev, drvdata);

	return 0;
}

最重要的工作就是进行consumer和regulator的匹配工作,也就是说给virtual-consumer找到对应的regulator。此处工作由函数devm_regulator_get()实现:

drivers/regulator/devres.c:
struct regulator *devm_regulator_get(struct device *dev, const char *id)
{
	return _devm_regulator_get(dev, id, NORMAL_GET);
}

--->

static struct regulator *_devm_regulator_get(struct device *dev, const char *id,
					     int get_type)
{
	struct regulator **ptr, *regulator;

	ptr = devres_alloc(devm_regulator_release, sizeof(*ptr), GFP_KERNEL);
	if (!ptr)
		return ERR_PTR(-ENOMEM);

	// gm 传入的是NORMAL_GET
	switch (get_type) {
	case NORMAL_GET:								// g, 通过NORMAL_GET的方式获取
		regulator = regulator_get(dev, id);
		break;
	case EXCLUSIVE_GET:
		regulator = regulator_get_exclusive(dev, id);
		break;
	case OPTIONAL_GET:
		regulator = regulator_get_optional(dev, id);
		break;
	default:
		regulator = ERR_PTR(-EINVAL);
	}

	if (!IS_ERR(regulator)) {
		*ptr = regulator;
		devres_add(dev, ptr);		// g, 把put函数加入到devres中,这样在remove模块的时候就可以自动调用put函数了
	} else {
		devres_free(ptr);
	}

	return regulator;
}

通过NORMAL_GET方式简历virtual-consumer和regulator_dev的联系,调用函数regulator_get():

drivers/regulator/core.c;
struct regulator *regulator_get(struct device *dev, const char *id)
{
	return _regulator_get(dev, id, false, true);
}

--->

static struct regulator *_regulator_get(struct device *dev, const char *id,
					bool exclusive, bool allow_dummy)	// g, false, true
{
	struct regulator_dev *rdev;
	struct regulator *regulator = ERR_PTR(-EPROBE_DEFER);
	const char *devname = NULL;
	int ret;

	if (id == NULL) {
		pr_err("get() with no identifier\n");
		return ERR_PTR(-EINVAL);
	}

	if (dev)
		devname = dev_name(dev);

	if (have_full_constraints())
		ret = -ENODEV;
	else
		ret = -EPROBE_DEFER;

	rdev = regulator_dev_lookup(dev, id, &ret);	// g, 这个函数会负责找到对应的regulator设备,也就是找到该consumer对应的struct regulator_dev
	if (rdev)
		goto found;

	regulator = ERR_PTR(ret);

	/*
	 * If we have return value from dev_lookup fail, we do not expect to
	 * succeed, so, quit with appropriate error value
	 */
	if (ret && ret != -ENODEV)
		return regulator;

	if (!devname)
		devname = "deviceless";

	/*
	 * Assume that a regulator is physically present and enabled
	 * even if it isn't hooked up and just provide a dummy.
	 */
	if (have_full_constraints() && allow_dummy) {
		pr_warn("%s supply %s not found, using dummy regulator\n",
			devname, id);

		rdev = dummy_regulator_rdev;
		get_device(&rdev->dev);
		goto found;
	/* Don't log an error when called from regulator_get_optional() */
	} else if (!have_full_constraints() || exclusive) {
		dev_warn(dev, "dummy supplies not allowed\n");
	}

	return regulator;

found:
	if (rdev->exclusive) {				// g, 如果有独占标志
		regulator = ERR_PTR(-EPERM);
		put_device(&rdev->dev);
		return regulator;
	}

	if (exclusive && rdev->open_count) {	// g, 如果想要独占regulator,并且已经被打开过了.实际上这里传入的exclusive = false, 所以这个if不起作用
		regulator = ERR_PTR(-EBUSY);
		put_device(&rdev->dev);
		return regulator;
	}

	ret = regulator_resolve_supply(rdev);
	if (ret < 0) {
		regulator = ERR_PTR(ret);
		put_device(&rdev->dev);
		return regulator;
	}

	if (!try_module_get(rdev->owner)) {
		put_device(&rdev->dev);
		return regulator;
	}

	// g, 如果上述检查都通过的话,会为regulator_dev创建一个struct regulator
	// g, 下面这个函数会将新创建的struct regulator加入到regulator_dev->consumer_list链表中
	// g, 并且初始化regulator的一些域,比如说使regulator->dev指向传入的consumer_dev
	// g, 会更新/sys观测目录下的一些观测节点,比如说在regulator_dev目录(/sys/class/regulator/regulator.x)下建立指向consumer_dev(xx/reg-virt-cosumer.3-id)的链接.
	// g, 会在rdev->debugfs(/sys/kernel/debug/regulator/xxx-aldo1)下建立一个调试文件夹regulator->supply_name(/sys/kernel/debug/regulator/xxx-aldo1/reg-virt-consumer.6-aldo1),里面有些调试结点
	regulator = create_regulator(rdev, dev, id);
	if (regulator == NULL) {
		regulator = ERR_PTR(-ENOMEM);
		put_device(&rdev->dev);
		module_put(rdev->owner);
		return regulator;
	}

	rdev->open_count++;
	if (exclusive) {
		rdev->exclusive = 1;

		ret = _regulator_is_enabled(rdev);
		if (ret > 0)
			rdev->use_count = 1;
		else
			rdev->use_count = 0;
	}

	return regulator;
}

在创建struct regulator的过程中,会调用regulator_dev_lookup()函数,该函数根据virual-consumer的信息,来找到对应的struct regulator_dev,也就是前面注册的regulator设备。其实现过程如下:

static struct regulator_dev *regulator_dev_lookup(struct device *dev,
						  const char *supply,
						  int *ret)
{
	struct regulator_dev *r;
	struct device_node *node;
	struct regulator_map *map;
	const char *devname = NULL;

	regulator_supply_alias(&dev, &supply);

	/* first do a dt based lookup */
	if (dev && dev->of_node) {
		node = of_get_regulator(dev, supply);		// g, 找到引用节点,也就是consumer节点要使用哪一个regulator节点,会在设备树中以引用节点的形式表达
		if (node) {
			r = of_find_regulator_by_node(node);	// g, 上面找到node后,获取对应的device,再通过device找到真正的regulator节点对应的struct regulator_dev
			if (r)
				return r;
			*ret = -EPROBE_DEFER;
			return NULL;
		} else {
			/*
			 * If we couldn't even get the node then it's
			 * not just that the device didn't register
			 * yet, there's no node and we'll never
			 * succeed.
			 */
			*ret = -ENODEV;
		}
	}

	/* if not found, try doing it non-dt way */
	// g, 如果上面没有找到,则不会return, 则需要换其他途径寻找.
	if (dev)
		devname = dev_name(dev);

	// g, 搜索方案二:从regulator_class下寻找是否有名为"supply"的device
	r = regulator_lookup_by_name(supply);
	if (r)
		return r;

	mutex_lock(&regulator_list_mutex);
	// g, 搜索方案三:从全局变量regulator_map_list中搜索,是否有名字能匹配的regulator_dev
	// g, 注册regulator_dev时会向regulator_map_list添加节点.
	list_for_each_entry(map, &regulator_map_list, list) {
		/* If the mapping has a device set up it must match */
		if (map->dev_name &&
		    (!devname || strcmp(map->dev_name, devname)))
			continue;

		if (strcmp(map->supply, supply) == 0 &&
		    get_device(&map->regulator->dev)) {
			mutex_unlock(&regulator_list_mutex);
			return map->regulator;
		}
	}
	mutex_unlock(&regulator_list_mutex);

	return NULL;
}

使用了三种匹配方案,第一种方案是通过设备树节点信息匹配,通过of_get_regulator()函数来找到对应的regulator对应的节点device_node:

drivers/regulator/core.c:
static struct device_node *of_get_regulator(struct device *dev, const char *supply)
{
	struct device_node *regnode = NULL;
	char prop_name[32]; /* 32 is max size of property name */

	dev_dbg(dev, "Looking up %s-supply from device tree\n", supply);

	snprintf(prop_name, 32, "%s-supply", supply);			// g, prop_name = dcdc1-supply

	// g, 该函数查找"dcdc1-supply"这个引用属性来找对应的结点
	// g, 比如说设备树中的一个virtual-consumer节点中的一个属性:dcdc1-supply = <®_dcdc1>;
	// g, 那么这个函数返回reg_dcdc1对应的节点.
	regnode = of_parse_phandle(dev->of_node, prop_name, 0);	

	if (!regnode) {
		dev_dbg(dev, "Looking up %s property in node %s failed",
				prop_name, dev->of_node->full_name);
		return NULL;
	}
	return regnode;
}

带of的函数都是与设备树有关的,该函数由regulator子系统实现,其中使用到了of_parse_phandle()函数。这个of函数是我第一次见,它的作用是根据引用节点名来寻找引用节点。所以为了使用第一种匹配方式,设备树中应这样实现:

	
				regulator0: regulators@0 {
					reg_dcdc1: dcdca {
						xxx = <xxx>
					};
					...
					// g, 其他regulator
					...
				};
				virtual-dcdc1 {
					...
					dcdc1-supply = <&reg_dcdc1>;			// g, 引用节点
					...
				};
				...
				// g, 其他虚拟consumer节点
				...

通过of_parse_phandle(virtual-dcdc1对应的dev_node,“dcdc1-supply”),就可以获取到regulator节点了。这种方法相当于提前在设备树中对virtual-consumer和regulator进行了绑定。

找到匹配的regulator对应的dev_node后,再通过of_find_regulator_by_node()函数找到对应的regulator_dev即可:

static struct regulator_dev *of_find_regulator_by_node(struct device_node *np)
{
	struct device *dev;

	dev = class_find_device(&regulator_class, NULL, np, of_node_match);	// g, 在regulator_class下找到node对应的device

	return dev ? dev_to_rdev(dev) : NULL;			// g, 找到该struct device所属的struct regulator_dev 
}

找到这个virtual-consumer对应的regulator_dev之后,接下来会创建struct regulator,创建struct regulator的函数为create_regulator(),就不展开说了,该函数实现的功能写在注释中了,主要就是把新创建的struct regulator添加到regulator_dev->consumer_list中,表示该struct regulator是该regulator_dev的一个consumer;并且为该virtual-consumer创建一些/sys下的调试节点。

这样,devm_regulator_get()的工作就完成了,再次之后我们调用regulator子系统提供的接口,都需要使用到刚刚创建的struct regulator,因为struct regulator->rdev域保存着匹配到的regulator_dev。

之后,就可以用virtual.c的probe过程中创建的调试节点,通过virtual-consumer来控制真正的regulator设备了。关于这些调试节点,就不说了,就是电流电压控制/开启关闭这些。

你可能感兴趣的:(linux,驱动开发,c语言)