162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * reg-virtual-consumer.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2008 Wolfson Microelectronics PLC.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/err.h>
1162306a36Sopenharmony_ci#include <linux/mutex.h>
1262306a36Sopenharmony_ci#include <linux/platform_device.h>
1362306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <linux/of.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistruct virtual_consumer_data {
1962306a36Sopenharmony_ci	struct mutex lock;
2062306a36Sopenharmony_ci	struct regulator *regulator;
2162306a36Sopenharmony_ci	bool enabled;
2262306a36Sopenharmony_ci	int min_uV;
2362306a36Sopenharmony_ci	int max_uV;
2462306a36Sopenharmony_ci	int min_uA;
2562306a36Sopenharmony_ci	int max_uA;
2662306a36Sopenharmony_ci	unsigned int mode;
2762306a36Sopenharmony_ci};
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic void update_voltage_constraints(struct device *dev,
3062306a36Sopenharmony_ci				       struct virtual_consumer_data *data)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	int ret;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	if (data->min_uV && data->max_uV
3562306a36Sopenharmony_ci	    && data->min_uV <= data->max_uV) {
3662306a36Sopenharmony_ci		dev_dbg(dev, "Requesting %d-%duV\n",
3762306a36Sopenharmony_ci			data->min_uV, data->max_uV);
3862306a36Sopenharmony_ci		ret = regulator_set_voltage(data->regulator,
3962306a36Sopenharmony_ci					data->min_uV, data->max_uV);
4062306a36Sopenharmony_ci		if (ret != 0) {
4162306a36Sopenharmony_ci			dev_err(dev,
4262306a36Sopenharmony_ci				"regulator_set_voltage() failed: %d\n", ret);
4362306a36Sopenharmony_ci			return;
4462306a36Sopenharmony_ci		}
4562306a36Sopenharmony_ci	}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	if (data->min_uV && data->max_uV && !data->enabled) {
4862306a36Sopenharmony_ci		dev_dbg(dev, "Enabling regulator\n");
4962306a36Sopenharmony_ci		ret = regulator_enable(data->regulator);
5062306a36Sopenharmony_ci		if (ret == 0)
5162306a36Sopenharmony_ci			data->enabled = true;
5262306a36Sopenharmony_ci		else
5362306a36Sopenharmony_ci			dev_err(dev, "regulator_enable() failed: %d\n",
5462306a36Sopenharmony_ci				ret);
5562306a36Sopenharmony_ci	}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	if (!(data->min_uV && data->max_uV) && data->enabled) {
5862306a36Sopenharmony_ci		dev_dbg(dev, "Disabling regulator\n");
5962306a36Sopenharmony_ci		ret = regulator_disable(data->regulator);
6062306a36Sopenharmony_ci		if (ret == 0)
6162306a36Sopenharmony_ci			data->enabled = false;
6262306a36Sopenharmony_ci		else
6362306a36Sopenharmony_ci			dev_err(dev, "regulator_disable() failed: %d\n",
6462306a36Sopenharmony_ci				ret);
6562306a36Sopenharmony_ci	}
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic void update_current_limit_constraints(struct device *dev,
6962306a36Sopenharmony_ci					  struct virtual_consumer_data *data)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	int ret;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	if (data->max_uA
7462306a36Sopenharmony_ci	    && data->min_uA <= data->max_uA) {
7562306a36Sopenharmony_ci		dev_dbg(dev, "Requesting %d-%duA\n",
7662306a36Sopenharmony_ci			data->min_uA, data->max_uA);
7762306a36Sopenharmony_ci		ret = regulator_set_current_limit(data->regulator,
7862306a36Sopenharmony_ci					data->min_uA, data->max_uA);
7962306a36Sopenharmony_ci		if (ret != 0) {
8062306a36Sopenharmony_ci			dev_err(dev,
8162306a36Sopenharmony_ci				"regulator_set_current_limit() failed: %d\n",
8262306a36Sopenharmony_ci				ret);
8362306a36Sopenharmony_ci			return;
8462306a36Sopenharmony_ci		}
8562306a36Sopenharmony_ci	}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	if (data->max_uA && !data->enabled) {
8862306a36Sopenharmony_ci		dev_dbg(dev, "Enabling regulator\n");
8962306a36Sopenharmony_ci		ret = regulator_enable(data->regulator);
9062306a36Sopenharmony_ci		if (ret == 0)
9162306a36Sopenharmony_ci			data->enabled = true;
9262306a36Sopenharmony_ci		else
9362306a36Sopenharmony_ci			dev_err(dev, "regulator_enable() failed: %d\n",
9462306a36Sopenharmony_ci				ret);
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	if (!(data->min_uA && data->max_uA) && data->enabled) {
9862306a36Sopenharmony_ci		dev_dbg(dev, "Disabling regulator\n");
9962306a36Sopenharmony_ci		ret = regulator_disable(data->regulator);
10062306a36Sopenharmony_ci		if (ret == 0)
10162306a36Sopenharmony_ci			data->enabled = false;
10262306a36Sopenharmony_ci		else
10362306a36Sopenharmony_ci			dev_err(dev, "regulator_disable() failed: %d\n",
10462306a36Sopenharmony_ci				ret);
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic ssize_t show_min_uV(struct device *dev,
10962306a36Sopenharmony_ci			   struct device_attribute *attr, char *buf)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	struct virtual_consumer_data *data = dev_get_drvdata(dev);
11262306a36Sopenharmony_ci	return sprintf(buf, "%d\n", data->min_uV);
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistatic ssize_t set_min_uV(struct device *dev, struct device_attribute *attr,
11662306a36Sopenharmony_ci			  const char *buf, size_t count)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	struct virtual_consumer_data *data = dev_get_drvdata(dev);
11962306a36Sopenharmony_ci	long val;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	if (kstrtol(buf, 10, &val) != 0)
12262306a36Sopenharmony_ci		return count;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	mutex_lock(&data->lock);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	data->min_uV = val;
12762306a36Sopenharmony_ci	update_voltage_constraints(dev, data);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	mutex_unlock(&data->lock);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	return count;
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cistatic ssize_t show_max_uV(struct device *dev,
13562306a36Sopenharmony_ci			   struct device_attribute *attr, char *buf)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	struct virtual_consumer_data *data = dev_get_drvdata(dev);
13862306a36Sopenharmony_ci	return sprintf(buf, "%d\n", data->max_uV);
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic ssize_t set_max_uV(struct device *dev, struct device_attribute *attr,
14262306a36Sopenharmony_ci			  const char *buf, size_t count)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	struct virtual_consumer_data *data = dev_get_drvdata(dev);
14562306a36Sopenharmony_ci	long val;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	if (kstrtol(buf, 10, &val) != 0)
14862306a36Sopenharmony_ci		return count;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	mutex_lock(&data->lock);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	data->max_uV = val;
15362306a36Sopenharmony_ci	update_voltage_constraints(dev, data);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	mutex_unlock(&data->lock);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	return count;
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic ssize_t show_min_uA(struct device *dev,
16162306a36Sopenharmony_ci			   struct device_attribute *attr, char *buf)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	struct virtual_consumer_data *data = dev_get_drvdata(dev);
16462306a36Sopenharmony_ci	return sprintf(buf, "%d\n", data->min_uA);
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cistatic ssize_t set_min_uA(struct device *dev, struct device_attribute *attr,
16862306a36Sopenharmony_ci			  const char *buf, size_t count)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	struct virtual_consumer_data *data = dev_get_drvdata(dev);
17162306a36Sopenharmony_ci	long val;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	if (kstrtol(buf, 10, &val) != 0)
17462306a36Sopenharmony_ci		return count;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	mutex_lock(&data->lock);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	data->min_uA = val;
17962306a36Sopenharmony_ci	update_current_limit_constraints(dev, data);
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	mutex_unlock(&data->lock);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	return count;
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic ssize_t show_max_uA(struct device *dev,
18762306a36Sopenharmony_ci			   struct device_attribute *attr, char *buf)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	struct virtual_consumer_data *data = dev_get_drvdata(dev);
19062306a36Sopenharmony_ci	return sprintf(buf, "%d\n", data->max_uA);
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cistatic ssize_t set_max_uA(struct device *dev, struct device_attribute *attr,
19462306a36Sopenharmony_ci			  const char *buf, size_t count)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	struct virtual_consumer_data *data = dev_get_drvdata(dev);
19762306a36Sopenharmony_ci	long val;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	if (kstrtol(buf, 10, &val) != 0)
20062306a36Sopenharmony_ci		return count;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	mutex_lock(&data->lock);
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	data->max_uA = val;
20562306a36Sopenharmony_ci	update_current_limit_constraints(dev, data);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	mutex_unlock(&data->lock);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	return count;
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic ssize_t show_mode(struct device *dev,
21362306a36Sopenharmony_ci			 struct device_attribute *attr, char *buf)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	struct virtual_consumer_data *data = dev_get_drvdata(dev);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	switch (data->mode) {
21862306a36Sopenharmony_ci	case REGULATOR_MODE_FAST:
21962306a36Sopenharmony_ci		return sprintf(buf, "fast\n");
22062306a36Sopenharmony_ci	case REGULATOR_MODE_NORMAL:
22162306a36Sopenharmony_ci		return sprintf(buf, "normal\n");
22262306a36Sopenharmony_ci	case REGULATOR_MODE_IDLE:
22362306a36Sopenharmony_ci		return sprintf(buf, "idle\n");
22462306a36Sopenharmony_ci	case REGULATOR_MODE_STANDBY:
22562306a36Sopenharmony_ci		return sprintf(buf, "standby\n");
22662306a36Sopenharmony_ci	default:
22762306a36Sopenharmony_ci		return sprintf(buf, "unknown\n");
22862306a36Sopenharmony_ci	}
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic ssize_t set_mode(struct device *dev, struct device_attribute *attr,
23262306a36Sopenharmony_ci			const char *buf, size_t count)
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	struct virtual_consumer_data *data = dev_get_drvdata(dev);
23562306a36Sopenharmony_ci	unsigned int mode;
23662306a36Sopenharmony_ci	int ret;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	/*
23962306a36Sopenharmony_ci	 * sysfs_streq() doesn't need the \n's, but we add them so the strings
24062306a36Sopenharmony_ci	 * will be shared with show_mode(), above.
24162306a36Sopenharmony_ci	 */
24262306a36Sopenharmony_ci	if (sysfs_streq(buf, "fast\n"))
24362306a36Sopenharmony_ci		mode = REGULATOR_MODE_FAST;
24462306a36Sopenharmony_ci	else if (sysfs_streq(buf, "normal\n"))
24562306a36Sopenharmony_ci		mode = REGULATOR_MODE_NORMAL;
24662306a36Sopenharmony_ci	else if (sysfs_streq(buf, "idle\n"))
24762306a36Sopenharmony_ci		mode = REGULATOR_MODE_IDLE;
24862306a36Sopenharmony_ci	else if (sysfs_streq(buf, "standby\n"))
24962306a36Sopenharmony_ci		mode = REGULATOR_MODE_STANDBY;
25062306a36Sopenharmony_ci	else {
25162306a36Sopenharmony_ci		dev_err(dev, "Configuring invalid mode\n");
25262306a36Sopenharmony_ci		return count;
25362306a36Sopenharmony_ci	}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	mutex_lock(&data->lock);
25662306a36Sopenharmony_ci	ret = regulator_set_mode(data->regulator, mode);
25762306a36Sopenharmony_ci	if (ret == 0)
25862306a36Sopenharmony_ci		data->mode = mode;
25962306a36Sopenharmony_ci	else
26062306a36Sopenharmony_ci		dev_err(dev, "Failed to configure mode: %d\n", ret);
26162306a36Sopenharmony_ci	mutex_unlock(&data->lock);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	return count;
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cistatic DEVICE_ATTR(min_microvolts, 0664, show_min_uV, set_min_uV);
26762306a36Sopenharmony_cistatic DEVICE_ATTR(max_microvolts, 0664, show_max_uV, set_max_uV);
26862306a36Sopenharmony_cistatic DEVICE_ATTR(min_microamps, 0664, show_min_uA, set_min_uA);
26962306a36Sopenharmony_cistatic DEVICE_ATTR(max_microamps, 0664, show_max_uA, set_max_uA);
27062306a36Sopenharmony_cistatic DEVICE_ATTR(mode, 0664, show_mode, set_mode);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic struct attribute *regulator_virtual_attributes[] = {
27362306a36Sopenharmony_ci	&dev_attr_min_microvolts.attr,
27462306a36Sopenharmony_ci	&dev_attr_max_microvolts.attr,
27562306a36Sopenharmony_ci	&dev_attr_min_microamps.attr,
27662306a36Sopenharmony_ci	&dev_attr_max_microamps.attr,
27762306a36Sopenharmony_ci	&dev_attr_mode.attr,
27862306a36Sopenharmony_ci	NULL
27962306a36Sopenharmony_ci};
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_cistatic const struct attribute_group regulator_virtual_attr_group = {
28262306a36Sopenharmony_ci	.attrs	= regulator_virtual_attributes,
28362306a36Sopenharmony_ci};
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci#ifdef CONFIG_OF
28662306a36Sopenharmony_cistatic const struct of_device_id regulator_virtual_consumer_of_match[] = {
28762306a36Sopenharmony_ci	{ .compatible = "regulator-virtual-consumer" },
28862306a36Sopenharmony_ci	{},
28962306a36Sopenharmony_ci};
29062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, regulator_virtual_consumer_of_match);
29162306a36Sopenharmony_ci#endif
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cistatic int regulator_virtual_probe(struct platform_device *pdev)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci	char *reg_id = dev_get_platdata(&pdev->dev);
29662306a36Sopenharmony_ci	struct virtual_consumer_data *drvdata;
29762306a36Sopenharmony_ci	static bool warned;
29862306a36Sopenharmony_ci	int ret;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	if (!warned) {
30162306a36Sopenharmony_ci		warned = true;
30262306a36Sopenharmony_ci		pr_warn("**********************************************************\n");
30362306a36Sopenharmony_ci		pr_warn("**   NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE   **\n");
30462306a36Sopenharmony_ci		pr_warn("**                                                      **\n");
30562306a36Sopenharmony_ci		pr_warn("** regulator-virtual-consumer is only for testing and   **\n");
30662306a36Sopenharmony_ci		pr_warn("** debugging.  Do not use it in a production kernel.    **\n");
30762306a36Sopenharmony_ci		pr_warn("**                                                      **\n");
30862306a36Sopenharmony_ci		pr_warn("**   NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE   **\n");
30962306a36Sopenharmony_ci		pr_warn("**********************************************************\n");
31062306a36Sopenharmony_ci	}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	drvdata = devm_kzalloc(&pdev->dev, sizeof(struct virtual_consumer_data),
31362306a36Sopenharmony_ci			       GFP_KERNEL);
31462306a36Sopenharmony_ci	if (drvdata == NULL)
31562306a36Sopenharmony_ci		return -ENOMEM;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	/*
31862306a36Sopenharmony_ci	 * This virtual consumer does not have any hardware-defined supply
31962306a36Sopenharmony_ci	 * name, so just allow the regulator to be specified in a property
32062306a36Sopenharmony_ci	 * named "default-supply" when we're being probed from devicetree.
32162306a36Sopenharmony_ci	 */
32262306a36Sopenharmony_ci	if (!reg_id && pdev->dev.of_node)
32362306a36Sopenharmony_ci		reg_id = "default";
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	mutex_init(&drvdata->lock);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	drvdata->regulator = devm_regulator_get(&pdev->dev, reg_id);
32862306a36Sopenharmony_ci	if (IS_ERR(drvdata->regulator))
32962306a36Sopenharmony_ci		return dev_err_probe(&pdev->dev, PTR_ERR(drvdata->regulator),
33062306a36Sopenharmony_ci				     "Failed to obtain supply '%s'\n",
33162306a36Sopenharmony_ci				     reg_id);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	ret = sysfs_create_group(&pdev->dev.kobj,
33462306a36Sopenharmony_ci				 &regulator_virtual_attr_group);
33562306a36Sopenharmony_ci	if (ret != 0) {
33662306a36Sopenharmony_ci		dev_err(&pdev->dev,
33762306a36Sopenharmony_ci			"Failed to create attribute group: %d\n", ret);
33862306a36Sopenharmony_ci		return ret;
33962306a36Sopenharmony_ci	}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	drvdata->mode = regulator_get_mode(drvdata->regulator);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	platform_set_drvdata(pdev, drvdata);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	return 0;
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistatic int regulator_virtual_remove(struct platform_device *pdev)
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci	struct virtual_consumer_data *drvdata = platform_get_drvdata(pdev);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	sysfs_remove_group(&pdev->dev.kobj, &regulator_virtual_attr_group);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	if (drvdata->enabled)
35562306a36Sopenharmony_ci		regulator_disable(drvdata->regulator);
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	return 0;
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_cistatic struct platform_driver regulator_virtual_consumer_driver = {
36162306a36Sopenharmony_ci	.probe		= regulator_virtual_probe,
36262306a36Sopenharmony_ci	.remove		= regulator_virtual_remove,
36362306a36Sopenharmony_ci	.driver		= {
36462306a36Sopenharmony_ci		.name		= "reg-virt-consumer",
36562306a36Sopenharmony_ci		.probe_type	= PROBE_PREFER_ASYNCHRONOUS,
36662306a36Sopenharmony_ci		.of_match_table = of_match_ptr(regulator_virtual_consumer_of_match),
36762306a36Sopenharmony_ci	},
36862306a36Sopenharmony_ci};
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cimodule_platform_driver(regulator_virtual_consumer_driver);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ciMODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
37362306a36Sopenharmony_ciMODULE_DESCRIPTION("Virtual regulator consumer");
37462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
37562306a36Sopenharmony_ciMODULE_ALIAS("platform:reg-virt-consumer");
376