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

Commit 48d920fe authored by Kyle Yan's avatar Kyle Yan Committed by Gerrit - the friendly Code Review server
Browse files

Merge "regulator: core: add debugfs regulator monitoring and control features" into msm-4.8

parents e5ec30d6 cea41357
Loading
Loading
Loading
Loading
+338 −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>
@@ -2199,7 +2201,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)
@@ -2308,6 +2314,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)
@@ -3426,7 +3434,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--;
@@ -3435,7 +3444,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++;
@@ -3863,11 +3873,269 @@ static struct class regulator_class = {
	.dev_groups = regulator_dev_groups,
};

#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;
	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->min_uV, reg->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) {
@@ -3888,8 +4156,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);
@@ -4096,8 +4431,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
@@ -29,6 +29,7 @@ struct regulator {
	int uA_load;
	int min_uV;
	int max_uV;
	int enabled;
	char *supply_name;
	struct device_attribute dev_attr;
	struct regulator_dev *rdev;
+2 −0
Original line number Diff line number Diff line
@@ -398,6 +398,7 @@ struct regulator_dev {
	int exclusive;
	u32 use_count;
	u32 open_count;
	u32 open_offset;
	u32 bypass_count;

	/* lists we belong to */
@@ -427,6 +428,7 @@ struct regulator_dev {

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

struct regulator_dev *