All of lore.kernel.org
 help / color / mirror / Atom feed
From: Brandon Leong <bleong@codeaurora.org>
To: lrg@slimlogic.co.uk, broonie@opensource.wolfsonmicro.com
Cc: dwalker@codeaurora.org, davidb@codeaurora.org,
	linux-arm-msm@vger.kernel.org,
	Brandon Leong <bleong@codeaurora.org>
Subject: [PATCHv2] regulator: debugfs: Adding debugfs functions into regulator framework
Date: Wed, 15 Dec 2010 17:49:05 -0800	[thread overview]
Message-ID: <1292464145-14204-1-git-send-email-bleong@codeaurora.org> (raw)

Allow the user to read and edit regulator information in user space
through the debugfs file system.

Signed-off-by: Brandon Leong <bleong@codeaurora.org>
---
Dynamic allocation of buffer added for debugfs.
Cleaned up error messages and stylistic errors.
Clean-up function created.

 drivers/regulator/core.c         |  403 ++++++++++++++++++++++++++++++++++++++
 include/linux/regulator/driver.h |    5 +
 2 files changed, 408 insertions(+), 0 deletions(-)

diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index ba521f0..50137c3 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -13,6 +13,8 @@
  *
  */
 
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/device.h>
@@ -21,6 +23,8 @@
 #include <linux/mutex.h>
 #include <linux/suspend.h>
 #include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
 #include <linux/regulator/consumer.h>
 #include <linux/regulator/driver.h>
 #include <linux/regulator/machine.h>
@@ -48,6 +52,20 @@ struct regulator_map {
 };
 
 /*
+ * struct regulator_debug
+ *
+ * Created for each regulator. Used specifically for debugfs purposes.
+ */
+struct regulator_debug {
+	struct regulator *reg;
+	struct regulator_dev *rdev;
+	struct dentry *reg_subdir;
+	int enabled;
+	char *debug_buffer;
+	int debug_buffer_len;
+};
+
+/*
  * struct regulator
  *
  * One for each consumer device.
@@ -2271,6 +2289,390 @@ static int add_regulator_attributes(struct regulator_dev *rdev)
 	return status;
 }
 
+#ifdef CONFIG_DEBUG_FS
+
+#define INIT_DEBUG_BUF_SIZE 20
+
+/* debug_buf_resize: dynamically resizes debug_buffer as needed */
+static char *debug_buf_resize(struct regulator_debug *reg_debug, int bufsize)
+{
+	if (bufsize <= reg_debug->debug_buffer_len)
+		return reg_debug->debug_buffer;
+
+	kfree(reg_debug->debug_buffer);
+
+	reg_debug->debug_buffer = kmalloc(bufsize, GFP_KERNEL);
+	reg_debug->debug_buffer_len = bufsize;
+
+	return reg_debug->debug_buffer;
+}
+
+static int reg_debug_enable_set(void *data, u64 val)
+{
+	int err_info = 0;
+	struct regulator_debug *reg_debug = data;
+
+	BUG_ON(data == NULL);
+
+	if (val && !(reg_debug->enabled))
+		err_info = regulator_enable(reg_debug->reg);
+	else if (!val && (reg_debug->enabled))
+		err_info = regulator_disable(reg_debug->reg);
+	else if (val && (reg_debug->enabled))
+		pr_err("regulator_enable has already been called for this regulator\n");
+	else
+		pr_err("regulator_disable has already been called for this regulator\n");
+
+	if (err_info < 0)
+		pr_err("regulator_enable/disable returned error: %d\n",
+			err_info);
+	else
+		reg_debug->enabled = val;
+
+	return 0;
+}
+
+static int reg_debug_enable_get(void *data, u64 *val)
+{
+	struct regulator_debug *reg_debug = data;
+
+	BUG_ON(data == NULL);
+
+	*val = regulator_is_enabled(reg_debug->reg);
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(reg_enable_fops, reg_debug_enable_get,
+			reg_debug_enable_set, "%llu\n");
+
+static int reg_debug_fdisable_set(void *data, u64 val)
+{
+	int err_info;
+	struct regulator_debug *reg_debug = data;
+
+	BUG_ON(data == NULL);
+
+	if (val > 0) {
+		err_info = regulator_force_disable(reg_debug->reg);
+		reg_debug->enabled = 0;
+	} else
+		err_info = 0;
+
+	if (err_info < 0)
+		pr_err("regulator_force_disable returned error: %d\n",
+			err_info);
+
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(reg_fdisable_fops, reg_debug_enable_get,
+			reg_debug_fdisable_set, "%llu\n");
+
+static ssize_t reg_debug_voltage_set(struct file *file, const char __user *buf,
+					size_t count, loff_t *ppos)
+{
+	int err_info, filled;
+	char *voltage_buffer;
+	struct regulator_debug *reg_debug = file->private_data;
+	int min, max = -1;
+
+	BUG_ON(file == NULL);
+
+	voltage_buffer = debug_buf_resize(reg_debug, count);
+
+	if (voltage_buffer == NULL) {
+		pr_err("debug_buf_resize returned NULL\n");
+		return -ENOMEM;
+	}
+
+	if (copy_from_user(voltage_buffer, (void __user *) buf, count))
+		return -EFAULT;
+
+	filled = sscanf(voltage_buffer, "%d %d", &min, &max);
+
+	/* check that user entered two integers */
+	if (filled < 2 || min < 0 || max < min) {
+		pr_info("Error, correct format: 'echo \"min max\" > voltage\n");
+		return -ENOMEM;
+	} else
+		err_info = regulator_set_voltage(reg_debug->reg, min, max);
+
+	if (err_info < 0)
+		pr_err("regulator_set_voltage returned error: %d\n", err_info);
+
+	return count;
+}
+
+static ssize_t reg_debug_voltage_get(struct file *file, char __user *buf,
+					size_t count, loff_t *ppos)
+{
+	int voltage, output;
+	char *voltage_buffer;
+	int rc = -1;
+	struct regulator_debug *reg_debug = file->private_data;
+
+	BUG_ON(file == NULL);
+
+	voltage = regulator_get_voltage(reg_debug->reg);
+
+	if (voltage < 0) {
+		pr_err("regulator_get_voltage returned error: %d", voltage);
+		return -ENOMEM;
+	} else {
+		voltage_buffer = debug_buf_resize(reg_debug,
+							INIT_DEBUG_BUF_SIZE);
+
+		output = snprintf(voltage_buffer, reg_debug->debug_buffer_len,
+					"%d\n", voltage);
+		rc = simple_read_from_buffer((void __user *) buf, output, ppos,
+				(void *) voltage_buffer, output);
+
+		return rc;
+	}
+}
+
+static int reg_debug_voltage_open(struct inode *inode, struct file *file)
+{
+	BUG_ON(file == NULL);
+
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static const struct file_operations reg_voltage_fops = {
+	.write	= reg_debug_voltage_set,
+	.open   = reg_debug_voltage_open,
+	.read	= reg_debug_voltage_get,
+};
+
+static int reg_debug_mode_set(void *data, u64 val)
+{
+	int err_info;
+	struct regulator_debug *reg_debug = data;
+
+	BUG_ON(data == NULL);
+
+	err_info = regulator_set_mode(reg_debug->reg, (unsigned int)val);
+
+	if (err_info < 0)
+		pr_err("regulator_set_mode returned error: %d\n", err_info);
+
+	return 0;
+}
+
+static int reg_debug_mode_get(void *data, u64 *val)
+{
+	int err_info;
+	struct regulator_debug *reg_debug = data;
+
+	BUG_ON(data == NULL);
+
+	err_info = regulator_get_mode(reg_debug->reg);
+
+	if (err_info < 0) {
+		pr_err("Regulator_get_mode returned error: %d\n", err_info);
+	} else {
+		*val = err_info;
+	}
+
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(reg_mode_fops, reg_debug_mode_get,
+			reg_debug_mode_set, "%llu\n");
+
+static int reg_debug_optimum_mode_set(void *data, u64 val)
+{
+	int err_info;
+	struct regulator_debug *reg_debug = data;
+
+	BUG_ON(data == NULL);
+
+	err_info = regulator_set_optimum_mode(reg_debug->reg,
+						(unsigned int)val);
+
+	if (err_info < 0) {
+		pr_err("Regulator_set_optimum_mode returned error: %d\n",
+			err_info);
+	}
+
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(reg_optimum_mode_fops, reg_debug_mode_get,
+			reg_debug_optimum_mode_set, "%llu\n");
+
+static struct dentry *debugfs_base;
+
+static int reg_debug_init(void)
+{
+	debugfs_base = debugfs_create_dir("regulator", NULL);
+	if (IS_ERR(debugfs_base) || debugfs_base == NULL) {
+		pr_err("Debugfs_create_dir PTR_ERR: %ld\n",
+			PTR_ERR(debugfs_base));
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void regulator_debugfs_cleanup(struct regulator_dev *rdev)
+{
+	BUG_ON(rdev == NULL);
+
+	regulator_put(rdev->debug_data->reg);
+	debugfs_remove_recursive(rdev->debug_data->reg_subdir);
+	kfree(rdev->debug_data->debug_buffer);
+	kfree(rdev->debug_data);
+}
+
+/**
+ * regulator_debug_create_directory - Creates debugfs directories and files
+ *					for each regulator
+ *
+ * @param regulator_dev Voltage/Current regulator class device.
+ *
+ * Description: This function is run everytime "regulator_register" is run.
+ *		It is called for every regulator and initilaizes both the
+ *		regulator directory and the data files inside (ex. enable,
+ *		get/set voltage etc).
+ */
+static int regulator_debug_create_directory(struct regulator_dev *regulator_dev)
+{
+	struct dentry *reg_subdir;
+	struct dentry *err_ptr;
+	struct regulator *reg;
+	struct regulator_ops *reg_ops;
+	struct regulator_debug *debugfs_info;
+	mode_t mode;
+
+	if (IS_ERR(regulator_dev) || regulator_dev == NULL) {
+		pr_err("regulator_dev PTR_ERR: %ld\n", PTR_ERR(regulator_dev));
+		goto error;
+	}
+
+	if (IS_ERR(debugfs_base) || debugfs_base == NULL) {
+		pr_err("debugfs_base PTR_ERR: %ld\n", PTR_ERR(debugfs_base));
+		goto error;
+	}
+
+	reg_subdir = debugfs_create_dir(regulator_dev->desc->name,
+					debugfs_base);
+
+	reg = regulator_get(NULL, regulator_dev->desc->name);
+	if (IS_ERR(reg) || reg == NULL) {
+		pr_err("regulator_get PTR_ERR: %ld\n", PTR_ERR(reg));
+		goto error;
+	}
+
+	debugfs_info = kzalloc(sizeof(struct regulator_debug), GFP_KERNEL);
+
+	if (debugfs_info == NULL) {
+		pr_err("debugfs_info is NULL after kzalloc\n");
+		goto error;
+	}
+
+	/* Populate debugfs_info */
+	debugfs_info->reg = reg;
+	debugfs_info->reg_subdir = reg_subdir;
+	debugfs_info->debug_buffer = NULL;
+
+	regulator_dev->debug_data = debugfs_info;
+
+	reg_ops = regulator_dev->desc->ops;
+
+	/* Enabled File */
+	mode = 0;
+	if (reg_ops->is_enabled)
+		mode |= S_IRUGO;
+	if (reg_ops->enable || reg_ops->disable)
+		mode |= S_IWUSR;
+	if (mode)
+		err_ptr = debugfs_create_file("enable", mode, reg_subdir,
+						debugfs_info, &reg_enable_fops);
+	if (IS_ERR(err_ptr)) {
+		pr_err("Failed to create enable file: %ld\n", PTR_ERR(err_ptr));
+		debugfs_remove_recursive(reg_subdir);
+		goto error;
+	}
+
+	/* Force-Disable File */
+	mode = 0;
+	if (reg_ops->is_enabled)
+		mode |= S_IRUGO;
+	if (reg_ops->enable || reg_ops->disable)
+		mode |= S_IWUSR;
+	if (mode)
+		err_ptr = debugfs_create_file("force_disable", mode, reg_subdir,
+					debugfs_info, &reg_fdisable_fops);
+	if (IS_ERR(err_ptr)) {
+		pr_err("Failed to create force_disable file: %ld\n",
+			PTR_ERR(err_ptr));
+		debugfs_remove_recursive(reg_subdir);
+		goto error;
+	}
+
+	/* Voltage File */
+	mode = 0;
+	if (reg_ops->get_voltage)
+		mode |= S_IRUGO;
+	if (reg_ops->set_voltage)
+		mode |= S_IWUSR;
+	if (mode)
+		err_ptr = debugfs_create_file("voltage", mode, reg_subdir,
+					debugfs_info, &reg_voltage_fops);
+	if (IS_ERR(err_ptr)) {
+		pr_err("Failed to create voltage file: %ld\n",
+			PTR_ERR(err_ptr));
+		debugfs_remove_recursive(reg_subdir);
+		goto error;
+	}
+
+	/* Mode File */
+	mode = 0;
+	if (reg_ops->get_mode)
+		mode |= S_IRUGO;
+	if (reg_ops->set_mode)
+		mode |= S_IWUSR;
+	if (mode)
+		err_ptr = debugfs_create_file("mode", mode, reg_subdir,
+						debugfs_info, &reg_mode_fops);
+	if (IS_ERR(err_ptr)) {
+		pr_err("Failed to create mode file: %ld\n", PTR_ERR(err_ptr));
+		debugfs_remove_recursive(reg_subdir);
+		goto error;
+	}
+
+	/* Optimum Mode File */
+	mode = 0;
+	if (reg_ops->get_mode)
+		mode |= S_IRUGO;
+	if (reg_ops->set_mode)
+		mode |= S_IWUSR;
+	if (mode)
+		err_ptr = debugfs_create_file("optimum_mode", mode, reg_subdir,
+					debugfs_info, &reg_optimum_mode_fops);
+	if (IS_ERR(err_ptr)) {
+		pr_err("Failed to create optimum_mode file: %ld\n",
+			PTR_ERR(err_ptr));
+		debugfs_remove_recursive(reg_subdir);
+		goto error;
+	}
+
+	return 0;
+
+error:
+	return -ENOMEM;
+}
+#else
+static inline void regulator_debug_create_directory(struct regulator_dev
+						*regulator_dev) { }
+
+static inline void reg_debug_init(void) { }
+
+static inline void regulator_debugfs_cleanup(struct regulator_dev *rdev) { }
+#endif
+
 /**
  * regulator_register - register regulator
  * @regulator_desc: regulator to register
@@ -2432,6 +2834,7 @@ void regulator_unregister(struct regulator_dev *rdev)
 
 	mutex_lock(&regulator_list_mutex);
 	WARN_ON(rdev->open_count);
+	regulator_debugfs_cleanup(rdev);
 	unset_regulator_supplies(rdev);
 	list_del(&rdev->list);
 	if (rdev->supply)
diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h
index 592cd7c..ce63090 100644
--- a/include/linux/regulator/driver.h
+++ b/include/linux/regulator/driver.h
@@ -20,6 +20,7 @@
 
 struct regulator_dev;
 struct regulator_init_data;
+struct regulator_debug;
 
 enum regulator_status {
 	REGULATOR_STATUS_OFF,
@@ -188,6 +189,10 @@ struct regulator_dev {
 	struct regulator_dev *supply;	/* for tree */
 
 	void *reg_data;		/* regulator_dev data */
+
+#ifdef CONFIG_DEBUG_FS
+	struct regulator_debug *debug_data;
+#endif
 };
 
 struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc,
-- 
1.7.3.1

-- 
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.

             reply	other threads:[~2010-12-16  1:49 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-12-16  1:49 Brandon Leong [this message]
2010-12-16 12:24 ` [PATCHv2] regulator: debugfs: Adding debugfs functions into regulator framework Mark Brown

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1292464145-14204-1-git-send-email-bleong@codeaurora.org \
    --to=bleong@codeaurora.org \
    --cc=broonie@opensource.wolfsonmicro.com \
    --cc=davidb@codeaurora.org \
    --cc=dwalker@codeaurora.org \
    --cc=linux-arm-msm@vger.kernel.org \
    --cc=lrg@slimlogic.co.uk \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.