regulator-fixed and regulator-gpio

1、regulator-fixed


作用:创建一个固定的 regulator。一般是一个 GPIO 控制了一路电,只有开(enable) \ 关(disabled)两种操作。


device-tree node

io_vdd_en: regulator-JW5217DFND {
	compatible = "regulator-fixed";
	pinctrl-names = "default";
	pinctrl-0 = <&io_vdd_en_pins_default>;
	gpios = <&wkup_gpio0 69 GPIO_ACTIVE_HIGH>;
	regulator-name = "jw5217dfnd";
	regulator-min-microvolt = <3300000>;
	regulator-max-microvolt = <3300000>;
	regulator-always-on;
	regulator-boot-on;
	enable-active-high;
	vin-supply = <&vsys_3v3>;
};


解析:

compatible = "regulator-fixed";

固定的 regulator。特点:不能控制电压,只能 enable 和 disabled,没设备用的时候自动关电(disabled)。相关代码如下:

// drivers/regulator/fixed.c

static const struct regulator_ops fixed_voltage_ops = {
};

static const struct regulator_ops fixed_voltage_clkenabled_ops = {
	.enable = reg_clock_enable,
	.disable = reg_clock_disable,
	.is_enabled = reg_clock_is_enabled,
};

static const struct of_device_id fixed_of_match[] = {
	{
		.compatible = "regulator-fixed",
		.data = &fixed_voltage_data,
	},
	{
		.compatible = "regulator-fixed-clock",
		.data = &fixed_clkenable_data,
	},
	{
	},
};

static struct platform_driver regulator_fixed_voltage_driver = {
	.probe		= reg_fixed_voltage_probe,
	.driver		= {
		.name		= "reg-fixed-voltage",
		.of_match_table = of_match_ptr(fixed_of_match),
	},
};

static int reg_fixed_voltage_probe(struct platform_device *pdev)
{
	struct fixed_voltage_data *drvdata;

	drvdata = devm_kzalloc(&pdev->dev, sizeof(struct fixed_voltage_data),
			       GFP_KERNEL);
 	...
	if (drvtype && drvtype->has_enable_clock) {
		drvdata->desc.ops = &fixed_voltage_clkenabled_ops;

		drvdata->enable_clock = devm_clk_get(dev, NULL);
		if (IS_ERR(drvdata->enable_clock)) {
			dev_err(dev, "Can't get enable-clock from devicetree\n");
			return -ENOENT;
		}
	} else {
		drvdata->desc.ops = &fixed_voltage_ops;
	}
	...

}

gpios = <&wkup_gpio0 69 GPIO_ACTIVE_HIGH>;

控制电的 GPIO。开电时(enabled)的将 GPIO 置为有效电平,关电时(disabled)置为无效电平。相关代码如下:

// drivers/regulator/fixed.c

static int reg_fixed_voltage_probe(struct platform_device *pdev)
{
	...
	cfg.ena_gpiod = gpiod_get_optional(&pdev->dev, NULL, gflags);
	if (IS_ERR(cfg.ena_gpiod))
		return PTR_ERR(cfg.ena_gpiod);
	...
}
// drivers/regulator/core.c

reg_fixed_voltage_probe
  -> devm_regulator_register
    -> regulator_register
      -> regulator_ena_gpio_request

static int regulator_ena_gpio_request(struct regulator_dev *rdev,
				const struct regulator_config *config)
{
	struct regulator_enable_gpio *pin, *new_pin;
	struct gpio_desc *gpiod;

	gpiod = config->ena_gpiod;
	new_pin = kzalloc(sizeof(*new_pin), GFP_KERNEL);
	...
	pin = new_pin;
	pin->gpiod = gpiod;
	rdev->ena_pin = pin;
	...
}

regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;

regulator 最小及最大电压限制。对于 regulator-fixed 无实际意义。


regulator-always-on;

一直开电,防止因其他原因被关电,否则需要在其他驱动中获取此 regulator 来手动控制:regulator_enable() \ regulator_disable()。
当指定了此选项后,会有一个的虚拟设备一直在使用此 regulator,可通过如下命令查看到:

cat /sys/class/regulator/regulator.*/num_users      # 查看有多少个设备在使用此 regulator
cat /sys/class/regulator/regulator.*/state          # 查看此 regulator 的状态:enabled or disabled

regulator-boot-on;

开机时自动上电。注意:若一段时间内无设备在使用此 regulator,则会自动关电(猜测应该和系统低功耗有关),因此必须加上 regulator-always-on。

相关代码如下:

// drivers/regulator/of_regulator.c

reg_fixed_voltage_probe
  -> of_get_fixed_voltage_config
    -> of_get_regulator_init_data
      -> of_get_regulation_constraints

static int of_get_regulation_constraints(struct device *dev,
					struct device_node *np,
					struct regulator_init_data **init_data,
					const struct regulator_desc *desc)
{
	struct regulation_constraints *constraints = &(*init_data)->constraints;
 	... 
	constraints->boot_on = of_property_read_bool(np, "regulator-boot-on");
	constraints->always_on = of_property_read_bool(np, "regulator-always-on");
	...
}

// drivers/regulator/core.c

reg_fixed_voltage_probe
  -> devm_regulator_register
    -> regulator_register
      -> set_machine_constraints

static int set_machine_constraints(struct regulator_dev *rdev)
{
	...
	if (rdev->constraints->always_on || rdev->constraints->boot_on) {
		/* If we want to enable this regulator, make sure that we know
		 * the supplying regulator.
		 */
		if (rdev->supply_name && !rdev->supply)
			return -EPROBE_DEFER;

		if (rdev->supply) {
			ret = regulator_enable(rdev->supply);
			if (ret < 0) {
				_regulator_put(rdev->supply);
				rdev->supply = NULL;
				return ret;
			}
		}

		ret = _regulator_do_enable(rdev);
		if (ret < 0 && ret != -EINVAL) {
			rdev_err(rdev, "failed to enable: %pe\n", ERR_PTR(ret));
			return ret;
		}

		if (rdev->constraints->always_on)
			rdev->use_count++;
	}
	...
}
// drivers/regulator/core.c

reg_fixed_voltage_probe
  -> devm_regulator_register
    -> regulator_register
      -> set_machine_constraints
        -> _regulator_do_enable

static int _regulator_do_enable(struct regulator_dev *rdev)
{
	...
	if (rdev->ena_pin) {
		if (!rdev->ena_gpio_state) {
			ret = regulator_ena_gpio_ctrl(rdev, true);
			if (ret < 0)
				return ret;
			rdev->ena_gpio_state = 1;
		}
	} else if (rdev->desc->ops->enable) {
		ret = rdev->desc->ops->enable(rdev);
		if (ret < 0)
			return ret;
	} else {
		return -EINVAL;
	}
	...
}
// drivers/regulator/core.c

reg_fixed_voltage_probe
  -> devm_regulator_register
    -> regulator_register
      -> set_machine_constraints
        -> _regulator_do_enable
          -> regulator_ena_gpio_ctrl

static int regulator_ena_gpio_ctrl(struct regulator_dev *rdev, bool enable)
{
	struct regulator_enable_gpio *pin = rdev->ena_pin;

	if (!pin)
		return -EINVAL;

	if (enable) {
		/* Enable GPIO at initial use */
		if (pin->enable_count == 0)
			gpiod_set_value_cansleep(pin->gpiod, 1);

		pin->enable_count++;
	} else {
		if (pin->enable_count > 1) {
			pin->enable_count--;
			return 0;
		}

		/* Disable GPIO if not used */
		if (pin->enable_count <= 1) {
			gpiod_set_value_cansleep(pin->gpiod, 0);
			pin->enable_count = 0;
		}
	}

	return 0;
}

自动关电的代码:

// drivers/regulator/core.c

regulator_init_complete_work_function
  -> regulator_late_cleanup
    -> _regulator_do_disable
      -> regulator_ena_gpio_ctrl
        -> gpiod_set_value_cansleep

static void regulator_init_complete_work_function(struct work_struct *work)
{
	/*
	 * Regulators may had failed to resolve their input supplies
	 * when were registered, either because the input supply was
	 * not registered yet or because its parent device was not
	 * bound yet. So attempt to resolve the input supplies for
	 * pending regulators before trying to disable unused ones.
	 */
	class_for_each_device(&regulator_class, NULL, NULL,
			      regulator_register_resolve_supply);

	/* If we have a full configuration then disable any regulators
	 * we have permission to change the status for and which are
	 * not in use or always_on.  This is effectively the default
	 * for DT and ACPI as they have full constraints.
	 */
	class_for_each_device(&regulator_class, NULL, NULL,
			      regulator_late_cleanup);
}

static DECLARE_DELAYED_WORK(regulator_init_complete_work,
			    regulator_init_complete_work_function);

enable-active-high;

指定 enable GPIO 的有效电平为高(默认为低),仅适用于 regulator。在这里,GPIO 属性中的 GPIO_ACTIVE_xxx 不起作用(建议两者设置成一致,否则会有警告)。相关代码如下:

// drivers/gpio/gpiolib-of.c

static void of_gpio_flags_quirks(struct device_node *np,
				 const char *propname,
				 enum of_gpio_flags *flags,
				 int index)
{
	/*
	 * Some GPIO fixed regulator quirks.
	 * Note that active low is the default.
	 */
	if (IS_ENABLED(CONFIG_REGULATOR) &&
	    (of_device_is_compatible(np, "regulator-fixed") ||
	     of_device_is_compatible(np, "reg-fixed-voltage") ||
	     (!(strcmp(propname, "enable-gpio") &&
		strcmp(propname, "enable-gpios")) &&
	      of_device_is_compatible(np, "regulator-gpio")))) {
		bool active_low = !of_property_read_bool(np,
							 "enable-active-high");
		/*
		 * The regulator GPIO handles are specified such that the
		 * presence or absence of "enable-active-high" solely controls
		 * the polarity of the GPIO line. Any phandle flags must
		 * be actively ignored.
		 */
		if ((*flags & OF_GPIO_ACTIVE_LOW) && !active_low) {
			pr_warn("%s GPIO handle specifies active low - ignored\n",
				of_node_full_name(np));
			*flags &= ~OF_GPIO_ACTIVE_LOW;
		}
		if (active_low)
			*flags |= OF_GPIO_ACTIVE_LOW;
	}
	...
}


2、regulator-gpio


作用:创建一个由 GPIO 控制的 regulator。一般是 GPIO 控制输出多个不同的电压或电流(支持多个 GPIO),因此除了开(enable) \ 关(disabled)两种操作外,往往还支持电压或电流的控制。


device-tree node

vdd_sd_dv: regulator-TLV71033 {
	compatible = "regulator-gpio";
	pinctrl-names = "default";
	pinctrl-0 = <&vdd_sd_dv_pins_default>;
	regulator-name = "tlv71033";
	regulator-min-microvolt = <1800000>;
	regulator-max-microvolt = <3300000>;
	regulator-type = voltage;
	regulator-boot-on;
	enable-active-high;
	vin-supply = <&vsys_5v0>;
	enable-gpios = <&wkup_gpio0 69 GPIO_ACTIVE_HIGH>;
	gpios = <&main_gpio0 8 GPIO_ACTIVE_HIGH>;
	gpios-states = <0>;
	states = <1800000 0x0>, <3300000 0x1>;
};


解析:

compatible = "regulator-gpio";

GPIO regulator。特点:通过 GPIO 来控制电压或电流。相关代码如下:

// drivers/regulator/gpio-regulator.c

static const struct regulator_ops gpio_regulator_voltage_ops = {
	.get_voltage = gpio_regulator_get_value,
	.set_voltage = gpio_regulator_set_voltage,
	.list_voltage = gpio_regulator_list_voltage,
};

static const struct regulator_ops gpio_regulator_current_ops = {
	.get_current_limit = gpio_regulator_get_value,
	.set_current_limit = gpio_regulator_set_current_limit,
};

static const struct of_device_id regulator_gpio_of_match[] = {
	{ .compatible = "regulator-gpio", },
	{},
};

static struct platform_driver gpio_regulator_driver = {
	.probe		= gpio_regulator_probe,
	.driver		= {
		.name		= "gpio-regulator",
		.of_match_table = of_match_ptr(regulator_gpio_of_match),
	},
};

static int gpio_regulator_probe(struct platform_device *pdev)
{
	...
	switch (config->type) {
	case REGULATOR_VOLTAGE:
		drvdata->desc.type = REGULATOR_VOLTAGE;
		drvdata->desc.ops = &gpio_regulator_voltage_ops;
		drvdata->desc.n_voltages = config->nr_states;
		break;
	case REGULATOR_CURRENT:
		drvdata->desc.type = REGULATOR_CURRENT;
		drvdata->desc.ops = &gpio_regulator_current_ops;
		break;
	default:
		dev_err(dev, "No regulator type set\n");
		return -EINVAL;
	}
	...
}


regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;

regulator 最小及最大电压限制。


regulator-type = voltage;

regulator 的类型,可选值:voltage(默认)、current。仅适用于 regulator-gpio。相关代码如下:

drivers/regulator/gpio-regulator.c

gpio_regulator_probe
  -> of_get_gpio_regulator_config

static struct gpio_regulator_config *
of_get_gpio_regulator_config(struct device *dev, struct device_node *np,
			     const struct regulator_desc *desc)
{
	...
	config->type = REGULATOR_VOLTAGE;
	ret = of_property_read_string(np, "regulator-type", &regtype);
	if (ret >= 0) {
		if (!strncmp("voltage", regtype, 7))
			config->type = REGULATOR_VOLTAGE;
		else if (!strncmp("current", regtype, 7))
			config->type = REGULATOR_CURRENT;
		else
			dev_warn(dev, "Unknown regulator-type '%s'\n",
				 regtype);
	}
	return config;
}

enable-gpios = <&wkup_gpio0 69 GPIO_ACTIVE_HIGH>;

与 regulator-fixed 不同,regulator-gpio 的 enabled 引脚由 enable-gpios 属性来指定,而不是 gpios 属性指定。

// drivers/regulator/gpio-regulator.c

static int gpio_regulator_probe(struct platform_device *pdev)
{
	...
	if (config->enabled_at_boot)
		gflags = GPIOD_OUT_HIGH | GPIOD_FLAGS_BIT_NONEXCLUSIVE;
	else
		gflags = GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_NONEXCLUSIVE;

	cfg.ena_gpiod = gpiod_get_optional(dev, "enable", gflags);
	if (IS_ERR(cfg.ena_gpiod))
		return PTR_ERR(cfg.ena_gpiod);
	...
}

gpios = <&main_gpio0 8 GPIO_ACTIVE_HIGH>;

用于控制控制输出多个不同的电压或电流的 GPIO。支持多个 GPIO。


gpios-states = <0>;

用于设置 GPIO (gpios)的默认输出电平,1 有效电平,0 无效电平(即设置 regulator 的默认输出电压或电流大小,保存在 gpio_regulator_data 结构体的 state 字段中)。gpios-states 属性中的元素和 gpios 属性中的元素一一对应。

相关代码如下:

// drivers/regulator/gpio-regulator.c

gpio_regulator_probe
  -> of_get_gpio_regulator_config

static struct gpio_regulator_config *
of_get_gpio_regulator_config(struct device *dev, struct device_node *np,
			     const struct regulator_desc *desc)
{
	struct gpio_regulator_config *config;
	int ngpios;

	config = devm_kzalloc(dev,
			sizeof(struct gpio_regulator_config),
			GFP_KERNEL);
	...
	ngpios = gpiod_count(dev, NULL);
	if (ngpios > 0) {
		config->gflags = devm_kzalloc(dev,
					      sizeof(enum gpiod_flags)
					      * ngpios,
					      GFP_KERNEL);
		if (!config->gflags)
			return ERR_PTR(-ENOMEM);

		for (i = 0; i < ngpios; i++) {
			u32 val;

			ret = of_property_read_u32_index(np, "gpios-states", i,
							 &val);

			/* Default to high per specification */
			if (ret)
				config->gflags[i] = GPIOD_OUT_HIGH;
			else
				config->gflags[i] =
					val ? GPIOD_OUT_HIGH : GPIOD_OUT_LOW;
		}
	}
	config->ngpios = ngpios;
	...
}

// drivers/regulator/gpio-regulator.c

static int gpio_regulator_probe(struct platform_device *pdev)
{
	struct gpio_regulator_data *drvdata;

	drvdata = devm_kzalloc(dev, sizeof(struct gpio_regulator_data),
			       GFP_KERNEL);
	if (drvdata == NULL)
		return -ENOMEM;

	if (np) {
		config = of_get_gpio_regulator_config(dev, np,
						      &drvdata->desc);
		if (IS_ERR(config))
			return PTR_ERR(config);
	}
	...
	drvdata->gpiods = devm_kzalloc(dev, sizeof(struct gpio_desc *),
				       GFP_KERNEL);
	if (!drvdata->gpiods)
		return -ENOMEM;
	for (i = 0; i < config->ngpios; i++) {
		drvdata->gpiods[i] = devm_gpiod_get_index(dev,
							  NULL,
							  i,
							  config->gflags[i]);
		if (IS_ERR(drvdata->gpiods[i]))
			return PTR_ERR(drvdata->gpiods[i]);
		/* This is good to know */
		gpiod_set_consumer_name(drvdata->gpiods[i], drvdata->desc.name);
	}
	drvdata->nr_gpios = config->ngpios;
	...
	state = 0;
	for (ptr = 0; ptr < drvdata->nr_gpios; ptr++) {
		if (config->gflags[ptr] == GPIOD_OUT_HIGH)
			state |= (1 << ptr);
	}
	drvdata->state = state;
	...
}

states = <1800000 0x0>, <3300000 0x1>;

此处就是 regulator-gpio 的核心。双数的元素代表电压或电流值,单数的元素代表所有 GPIO 的输出电平二进制组合编码后的值。相关代码如下:

// drivers/regulator/gpio-regulator.c

gpio_regulator_probe
  -> of_get_gpio_regulator_config

static struct gpio_regulator_config *
of_get_gpio_regulator_config(struct device *dev, struct device_node *np,
			     const struct regulator_desc *desc)
{
	...
	proplen = of_property_count_u32_elems(np, "states");
	if (proplen < 0) {
		dev_err(dev, "No 'states' property found\n");
		return ERR_PTR(-EINVAL);
	}

	config->states = devm_kcalloc(dev,
				proplen / 2,
				sizeof(struct gpio_regulator_state),
				GFP_KERNEL);
	if (!config->states)
		return ERR_PTR(-ENOMEM);

	for (i = 0; i < proplen / 2; i++) {
		of_property_read_u32_index(np, "states", i * 2,
					   &config->states[i].value);
		of_property_read_u32_index(np, "states", i * 2 + 1,
					   &config->states[i].gpios);
	}
	config->nr_states = i;
	...
}
// drivers/regulator/gpio-regulator.c

static int gpio_regulator_probe(struct platform_device *pdev)
{
	...
	drvdata->states = devm_kmemdup(dev,
				       config->states,
				       config->nr_states *
				       sizeof(struct gpio_regulator_state),
				       GFP_KERNEL);
	if (drvdata->states == NULL) {
		dev_err(dev, "Failed to allocate state data\n");
		return -ENOMEM;
	}
	drvdata->nr_states = config->nr_states;
	...
}

获取及设置电压或电流的代码如下:

static int gpio_regulator_get_value(struct regulator_dev *dev)
{
	struct gpio_regulator_data *data = rdev_get_drvdata(dev);
	int ptr;

	for (ptr = 0; ptr < data->nr_states; ptr++)
		if (data->states[ptr].gpios == data->state)
			return data->states[ptr].value;

	return -EINVAL;
}

static int gpio_regulator_set_voltage(struct regulator_dev *dev,
					int min_uV, int max_uV,
					unsigned *selector)
{
	struct gpio_regulator_data *data = rdev_get_drvdata(dev);
	int ptr, target = 0, state, best_val = INT_MAX;

	for (ptr = 0; ptr < data->nr_states; ptr++)
		if (data->states[ptr].value < best_val &&
		    data->states[ptr].value >= min_uV &&
		    data->states[ptr].value <= max_uV) {
			target = data->states[ptr].gpios;
			best_val = data->states[ptr].value;
			if (selector)
				*selector = ptr;
		}

	if (best_val == INT_MAX)
		return -EINVAL;

	for (ptr = 0; ptr < data->nr_gpios; ptr++) {
		state = (target & (1 << ptr)) >> ptr;
		gpiod_set_value_cansleep(data->gpiods[ptr], state);
	}
	data->state = target;

	return 0;
}

static int gpio_regulator_set_current_limit(struct regulator_dev *dev,
					int min_uA, int max_uA)
{
	struct gpio_regulator_data *data = rdev_get_drvdata(dev);
	int ptr, target = 0, state, best_val = 0;

	for (ptr = 0; ptr < data->nr_states; ptr++)
		if (data->states[ptr].value > best_val &&
		    data->states[ptr].value >= min_uA &&
		    data->states[ptr].value <= max_uA) {
			target = data->states[ptr].gpios;
			best_val = data->states[ptr].value;
		}

	if (best_val == 0)
		return -EINVAL;

	for (ptr = 0; ptr < data->nr_gpios; ptr++) {
		state = (target & (1 << ptr)) >> ptr;
		gpiod_set_value_cansleep(data->gpiods[ptr], state);
	}
	data->state = target;

	return 0;
}

根据以上代码可知设置电压或电流时,设置的是一个范围值,而不是具体值。
代码处理逻辑:
1、遍历所有状态(device-tree node 中的 states 属性),找到符合要求的最好值(对于电压就是符合要求的最小电压值;对于电流就是符合要求的最大电流值),保存最好的电压或电流值及 GPIO 输出电平编码值。
2、根据编码值设置所有 GPIO 的输出电平。GPIO 的顺序和二进制顺序一致,比如第 1 个 GPIO 对应第 1 位。

posted @ 2023-08-28 19:54  Ma-ZhiQiang  阅读(2335)  评论(0编辑  收藏  举报