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

Commit 1dff8619 authored by David Collins's avatar David Collins
Browse files

regulator: core: add debugfs regulator monitoring and control features



Add several debugfs files for each registered regulator which
allow for monitoring of the regulator state and the requests from
each consumer.  Also configure these files as writeable so that a
debug consumer requested per-regulator can easily vote on the
regulator state.

This commit is a squash of the following msm-4.4 branch commits
with checkpatch warnings and other minor issues resolved:

commit 32c846e998a1 ("regulator: debugfs: Adding debugfs
                      functions into regulator framework")
commit db58e9bdc318 ("regulator: core: fix regulator bypass
                      logic")
commit 40456168dbe8 ("regulator: core: put debugfs consumer
                      handle when unregistering a regulator")
commit a118e09f3ce4 ("regulator: core: correct double remove
                      in rdev_deinit_debugfs")
commit 7ff3a4cc82e7 ("regulator: correct name used in debug
                      consumer regulator_get call")

Change-Id: Ia13b8f698868374499bb9be390ade4519c517e29
Signed-off-by: default avatarDavid Collins <collinsd@codeaurora.org>
parent 50cdbd7a
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 *