Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a3b788ec authored by qctecmdr Service's avatar qctecmdr Service Committed by Gerrit - the friendly Code Review server
Browse files

Merge "regulator: core: add debugfs regulator monitoring and control features"

parents 34485b98 1dff8619
Loading
Loading
Loading
Loading
+340 −3
Original line number Diff line number Diff line
@@ -27,6 +27,8 @@
#include <linux/gpio/consumer.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/consumer.h>
#include <linux/regulator/driver.h>
@@ -2283,7 +2285,11 @@ int regulator_enable(struct regulator *regulator)
	}

	mutex_lock(&rdev->mutex);

	ret = _regulator_enable(rdev);
	if (ret == 0)
		regulator->enabled++;

	mutex_unlock(&rdev->mutex);

	if (ret != 0 && rdev->supply)
@@ -2392,6 +2398,8 @@ int regulator_disable(struct regulator *regulator)

	mutex_lock(&rdev->mutex);
	ret = _regulator_disable(rdev);
	if (ret == 0)
		regulator->enabled--;
	mutex_unlock(&rdev->mutex);

	if (ret == 0 && rdev->supply)
@@ -3679,7 +3687,8 @@ int regulator_allow_bypass(struct regulator *regulator, bool enable)
	if (enable && !regulator->bypass) {
		rdev->bypass_count++;

		if (rdev->bypass_count == rdev->open_count) {
		if (rdev->bypass_count == rdev->open_count -
		    rdev->open_offset) {
			ret = rdev->desc->ops->set_bypass(rdev, enable);
			if (ret != 0)
				rdev->bypass_count--;
@@ -3688,7 +3697,8 @@ int regulator_allow_bypass(struct regulator *regulator, bool enable)
	} else if (!enable && regulator->bypass) {
		rdev->bypass_count--;

		if (rdev->bypass_count != rdev->open_count) {
		if (rdev->bypass_count != rdev->open_count -
		    rdev->open_offset) {
			ret = rdev->desc->ops->set_bypass(rdev, enable);
			if (ret != 0)
				rdev->bypass_count++;
@@ -4106,11 +4116,271 @@ static void regulator_dev_release(struct device *dev)
	kfree(rdev);
}

#ifdef CONFIG_DEBUG_FS

static int reg_debug_enable_set(void *data, u64 val)
{
	struct regulator *regulator = data;
	int ret;

	if (val) {
		ret = regulator_enable(regulator);
		if (ret)
			rdev_err(regulator->rdev, "enable failed, ret=%d\n",
				ret);
	} else {
		ret = regulator_disable(regulator);
		if (ret)
			rdev_err(regulator->rdev, "disable failed, ret=%d\n",
				ret);
	}

	return ret;
}

static int reg_debug_enable_get(void *data, u64 *val)
{
	struct regulator *regulator = data;

	*val = regulator_is_enabled(regulator);

	return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(reg_enable_fops, reg_debug_enable_get,
			reg_debug_enable_set, "%llu\n");

static int reg_debug_bypass_enable_get(void *data, u64 *val)
{
	struct regulator *regulator = data;
	struct regulator_dev *rdev = regulator->rdev;
	bool enable = false;
	int ret = 0;

	mutex_lock(&rdev->mutex);
	if (rdev->desc->ops->get_bypass) {
		ret = rdev->desc->ops->get_bypass(rdev, &enable);
		if (ret)
			rdev_err(rdev, "get_bypass() failed, ret=%d\n", ret);
	} else {
		enable = (rdev->bypass_count == rdev->open_count
			  - rdev->open_offset);
	}
	mutex_unlock(&rdev->mutex);

	*val = enable;

	return ret;
}

static int reg_debug_bypass_enable_set(void *data, u64 val)
{
	struct regulator *regulator = data;
	struct regulator_dev *rdev = regulator->rdev;
	int ret = 0;

	mutex_lock(&rdev->mutex);
	rdev->open_offset = 0;
	mutex_unlock(&rdev->mutex);

	ret = regulator_allow_bypass(data, val);

	return ret;
}
DEFINE_SIMPLE_ATTRIBUTE(reg_bypass_enable_fops, reg_debug_bypass_enable_get,
			reg_debug_bypass_enable_set, "%llu\n");

static int reg_debug_force_disable_set(void *data, u64 val)
{
	struct regulator *regulator = data;
	int ret = 0;

	if (val > 0) {
		ret = regulator_force_disable(regulator);
		if (ret)
			rdev_err(regulator->rdev, "force_disable failed, ret=%d\n",
				ret);
	}

	return ret;
}
DEFINE_SIMPLE_ATTRIBUTE(reg_force_disable_fops, reg_debug_enable_get,
			reg_debug_force_disable_set, "%llu\n");

#define MAX_DEBUG_BUF_LEN 50

static ssize_t reg_debug_voltage_write(struct file *file,
			const char __user *ubuf, size_t count, loff_t *ppos)
{
	struct regulator *regulator = file->private_data;
	char buf[MAX_DEBUG_BUF_LEN];
	int ret, filled;
	int min_uV, max_uV = -1;

	if (count < MAX_DEBUG_BUF_LEN) {
		if (copy_from_user(buf, ubuf, count))
			return -EFAULT;

		buf[count] = '\0';
		filled = sscanf(buf, "%d %d", &min_uV, &max_uV);

		/* Check that both min and max voltage were specified. */
		if (filled < 2 || min_uV < 0 || max_uV < min_uV) {
			rdev_err(regulator->rdev, "incorrect values specified: \"%s\"; should be: \"min_uV max_uV\"\n",
				buf);
			return -EINVAL;
		}

		ret = regulator_set_voltage(regulator, min_uV, max_uV);
		if (ret) {
			rdev_err(regulator->rdev, "set voltage(%d, %d) failed, ret=%d\n",
				min_uV, max_uV, ret);
			return ret;
		}
	} else {
		rdev_err(regulator->rdev, "voltage request string exceeds maximum buffer size\n");
		return -EINVAL;
	}

	return count;
}

static ssize_t reg_debug_voltage_read(struct file *file, char __user *ubuf,
					size_t count, loff_t *ppos)
{
	struct regulator *regulator = file->private_data;
	char buf[MAX_DEBUG_BUF_LEN];
	int voltage, ret;

	voltage = regulator_get_voltage(regulator);

	ret = snprintf(buf, MAX_DEBUG_BUF_LEN - 1, "%d\n", voltage);

	return simple_read_from_buffer(ubuf, count, ppos, buf, ret);
}

static int reg_debug_voltage_open(struct inode *inode, struct file *file)
{
	file->private_data = inode->i_private;

	return 0;
}

static const struct file_operations reg_voltage_fops = {
	.write	= reg_debug_voltage_write,
	.open	= reg_debug_voltage_open,
	.read	= reg_debug_voltage_read,
};

static int reg_debug_mode_set(void *data, u64 val)
{
	struct regulator *regulator = data;
	unsigned int mode = val;
	int ret;

	ret = regulator_set_mode(regulator, mode);
	if (ret)
		rdev_err(regulator->rdev, "set mode=%u failed, ret=%d\n",
			mode, ret);

	return ret;
}

static int reg_debug_mode_get(void *data, u64 *val)
{
	struct regulator *regulator = data;
	int mode;

	mode = regulator_get_mode(regulator);
	if (mode < 0) {
		rdev_err(regulator->rdev, "get mode failed, ret=%d\n", mode);
		return mode;
	}

	*val = mode;

	return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(reg_mode_fops, reg_debug_mode_get, reg_debug_mode_set,
			"%llu\n");

static int reg_debug_set_load(void *data, u64 val)
{
	struct regulator *regulator = data;
	int load = val;
	int ret;

	ret = regulator_set_load(regulator, load);
	if (ret)
		rdev_err(regulator->rdev, "set load=%d failed, ret=%d\n",
			load, ret);

	return ret;
}
DEFINE_SIMPLE_ATTRIBUTE(reg_set_load_fops, reg_debug_mode_get,
			reg_debug_set_load, "%llu\n");

static int reg_debug_consumers_show(struct seq_file *m, void *v)
{
	struct regulator_dev *rdev = m->private;
	struct regulator *reg;
	const char *supply_name;

	mutex_lock(&rdev->mutex);

	/* Print a header if there are consumers. */
	if (rdev->open_count)
		seq_printf(m, "%-32s EN    Min_uV   Max_uV  load_uA\n",
			"Device-Supply");

	list_for_each_entry(reg, &rdev->consumer_list, list) {
		if (reg->supply_name)
			supply_name = reg->supply_name;
		else
			supply_name = "(null)-(null)";

		seq_printf(m, "%-32s %c   %8d %8d %8d\n", supply_name,
			(reg->enabled ? 'Y' : 'N'),
			reg->voltage[PM_SUSPEND_ON].min_uV,
			reg->voltage[PM_SUSPEND_ON].max_uV,
			reg->uA_load);
	}

	mutex_unlock(&rdev->mutex);

	return 0;
}

static int reg_debug_consumers_open(struct inode *inode, struct file *file)
{
	return single_open(file, reg_debug_consumers_show, inode->i_private);
}

static const struct file_operations reg_consumers_fops = {
	.owner		= THIS_MODULE,
	.open		= reg_debug_consumers_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= single_release,
};

static void rdev_deinit_debugfs(struct regulator_dev *rdev)
{
	if (!IS_ERR_OR_NULL(rdev)) {
		debugfs_remove_recursive(rdev->debugfs);
		if (rdev->debug_consumer)
			rdev->debug_consumer->debugfs = NULL;
		regulator_put(rdev->debug_consumer);
	}
}

static void rdev_init_debugfs(struct regulator_dev *rdev)
{
	struct device *parent = rdev->dev.parent;
	const char *rname = rdev_get_name(rdev);
	char name[NAME_MAX];
	struct regulator *regulator;
	const struct regulator_ops *ops;
	mode_t mode;

	/* Avoid duplicate debugfs directory names */
	if (parent && rname == rdev->desc->name) {
@@ -4131,8 +4401,75 @@ static void rdev_init_debugfs(struct regulator_dev *rdev)
			   &rdev->open_count);
	debugfs_create_u32("bypass_count", 0444, rdev->debugfs,
			   &rdev->bypass_count);
	debugfs_create_file("consumers", 0444, rdev->debugfs, rdev,
			    &reg_consumers_fops);

	regulator = regulator_get(NULL, rdev_get_name(rdev));
	if (IS_ERR(regulator)) {
		rdev_err(rdev, "regulator get failed, ret=%ld\n",
			PTR_ERR(regulator));
		return;
	}
	rdev->debug_consumer = regulator;

	rdev->open_offset = 1;
	ops = rdev->desc->ops;

	debugfs_create_file("enable", 0644, rdev->debugfs, regulator,
				&reg_enable_fops);
	if (ops->set_bypass)
		debugfs_create_file("bypass", 0644, rdev->debugfs, regulator,
					&reg_bypass_enable_fops);

	mode = 0;
	if (ops->is_enabled)
		mode |= 0444;
	if (ops->disable)
		mode |= 0200;
	if (mode)
		debugfs_create_file("force_disable", mode, rdev->debugfs,
					regulator, &reg_force_disable_fops);

	mode = 0;
	if (ops->get_voltage || ops->get_voltage_sel)
		mode |= 0444;
	if (ops->set_voltage || ops->set_voltage_sel)
		mode |= 0200;
	if (mode)
		debugfs_create_file("voltage", mode, rdev->debugfs, regulator,
					&reg_voltage_fops);

	mode = 0;
	if (ops->get_mode)
		mode |= 0444;
	if (ops->set_mode)
		mode |= 0200;
	if (mode)
		debugfs_create_file("mode", mode, rdev->debugfs, regulator,
					&reg_mode_fops);

	mode = 0;
	if (ops->get_mode)
		mode |= 0444;
	if (ops->set_load || (ops->get_optimum_mode && ops->set_mode))
		mode |= 0200;
	if (mode)
		debugfs_create_file("load", mode, rdev->debugfs, regulator,
					&reg_set_load_fops);
}

#else

static inline void rdev_deinit_debugfs(struct regulator_dev *rdev)
{
}

static inline void rdev_init_debugfs(struct regulator_dev *rdev)
{
}

#endif

static int regulator_register_resolve_supply(struct device *dev, void *data)
{
	struct regulator_dev *rdev = dev_to_rdev(dev);
@@ -4442,8 +4779,8 @@ void regulator_unregister(struct regulator_dev *rdev)
			regulator_disable(rdev->supply);
		regulator_put(rdev->supply);
	}
	rdev_deinit_debugfs(rdev);
	mutex_lock(&regulator_list_mutex);
	debugfs_remove_recursive(rdev->debugfs);
	flush_work(&rdev->disable_work.work);
	WARN_ON(rdev->open_count);
	unset_regulator_supplies(rdev);
+1 −0
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ struct regulator {
	unsigned int bypass:1;
	int uA_load;
	struct regulator_voltage voltage[REGULATOR_STATES_NUM];
	int enabled;
	const char *supply_name;
	struct device_attribute dev_attr;
	struct regulator_dev *rdev;
+2 −0
Original line number Diff line number Diff line
@@ -440,6 +440,7 @@ struct regulator_dev {
	int exclusive;
	u32 use_count;
	u32 open_count;
	u32 open_offset;
	u32 bypass_count;

	/* lists we belong to */
@@ -475,6 +476,7 @@ struct regulator_dev {

	/* time when this regulator was disabled last time */
	unsigned long last_off_jiffy;
	struct regulator *debug_consumer;
};

struct regulator_dev *