Allow the user to read and edit regulator information in user space through the debugfs file system. Signed-off-by: Brandon Leong <bleong@xxxxxxxxxxxxxx> --- drivers/regulator/core.c | 335 ++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 335 insertions(+), 0 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index e63366f..2004e17 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -23,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> @@ -2270,6 +2272,336 @@ static int add_regulator_attributes(struct regulator_dev *rdev) return status; } +#ifdef CONFIG_DEBUG_FS + +#define MAX_DEBUG_BUF_LEN 50 + +static DEFINE_MUTEX(debug_buf_mutex); +static char debug_buf[MAX_DEBUG_BUF_LEN]; + +static int reg_debug_enable_set(void *data, u64 val) +{ + int err_info; + if (IS_ERR(data) || data == NULL) { + pr_err("Function Input Error %ld\n", PTR_ERR(data)); + return -ENOMEM; + } + + if (val) + err_info = regulator_enable(data); + else + err_info = regulator_disable(data); + + return err_info; +} + +static int reg_debug_enable_get(void *data, u64 *val) +{ + if (IS_ERR(data) || data == NULL) { + pr_err("Function Input Error %ld\n", PTR_ERR(data)); + return -ENOMEM; + } + + *val = regulator_is_enabled(data); + 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; + if (IS_ERR(data) || data == NULL) { + pr_err("Function Input Error %ld\n", PTR_ERR(data)); + return -ENOMEM; + } + + if (val > 0) + err_info = regulator_force_disable(data); + else + err_info = 0; + + return err_info; +} + +DEFINE_SIMPLE_ATTRIBUTE(reg_fdisable_fops, reg_debug_enable_get, + reg_debug_fdisable_set, "%llu\n"); + +static ssize_t reg_debug_volt_set(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + int err_info, filled; + int min, max = -1; + if (IS_ERR(file) || file == NULL) { + pr_err("Function Input Error %ld\n", PTR_ERR(file)); + return -ENOMEM; + } + + if (count < MAX_DEBUG_BUF_LEN) { + mutex_lock(&debug_buf_mutex); + + if (copy_from_user(debug_buf, (void __user *) buf, count)) + return -EFAULT; + + debug_buf[count] = '\0'; + filled = sscanf(debug_buf, "%d %d", &min, &max); + + mutex_unlock(&debug_buf_mutex); + /* check that user entered two numbers */ + if (filled < 2 || min < 0 || max < min) { + pr_info("Error, correct format: 'echo \"min max\"" + " > voltage"); + return -ENOMEM; + } else { + err_info = regulator_set_voltage(file->private_data, + min, max); + } + } else { + pr_err("Error-Input voltage pair" + "string exceeds maximum buffer length"); + + return -ENOMEM; + } + + return count; +} + +static ssize_t reg_debug_volt_get(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + int voltage, output, rc; + if (IS_ERR(file) || file == NULL) { + pr_err("Function Input Error %ld\n", PTR_ERR(file)); + return -ENOMEM; + } + + voltage = regulator_get_voltage(file->private_data); + mutex_lock(&debug_buf_mutex); + + output = snprintf(debug_buf, MAX_DEBUG_BUF_LEN-1, "%d\n", voltage); + rc = simple_read_from_buffer((void __user *) buf, output, ppos, + (void *) debug_buf, output); + + mutex_unlock(&debug_buf_mutex); + + return rc; +} + +static int reg_debug_volt_open(struct inode *inode, struct file *file) +{ + if (IS_ERR(file) || file == NULL) { + pr_err("Function Input Error %ld\n", PTR_ERR(file)); + return -ENOMEM; + } + + file->private_data = inode->i_private; + return 0; +} + +static const struct file_operations reg_volt_fops = { + .write = reg_debug_volt_set, + .open = reg_debug_volt_open, + .read = reg_debug_volt_get, +}; + +static int reg_debug_mode_set(void *data, u64 val) +{ + int err_info; + if (IS_ERR(data) || data == NULL) { + pr_err("Function Input Error %ld\n", PTR_ERR(data)); + return -ENOMEM; + } + + err_info = regulator_set_mode(data, (unsigned int)val); + + return err_info; +} + +static int reg_debug_mode_get(void *data, u64 *val) +{ + int err_info; + if (IS_ERR(data) || data == NULL) { + pr_err("Function Input Error %ld\n", PTR_ERR(data)); + return -ENOMEM; + } + + err_info = regulator_get_mode(data); + + if (err_info < 0) { + pr_err("regulator_get_mode returned an error!\n"); + return -ENOMEM; + } 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; + if (IS_ERR(data) || data == NULL) { + pr_err("Function Input Error %ld\n", PTR_ERR(data)); + return -ENOMEM; + } + + err_info = regulator_set_optimum_mode(data, (unsigned int)val); + + if (err_info < 0) { + pr_err("regulator_set_optimum_mode returned an error!\n"); + return 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 returned error" + " %ld\n", PTR_ERR(debugfs_base)); + return -ENOMEM; + } + + return 0; +} + +/** + * 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; + mode_t mode; + if (IS_ERR(regulator_dev) || regulator_dev == NULL || + IS_ERR(debugfs_base) || debugfs_base == NULL) { + pr_err("Error-Bad Input\n"); + 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("Error-Bad Input\n"); + goto error; + } + + reg_ops = regulator_dev->desc->ops; + mode = 0; + /* Enabled File */ + 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, + reg, ®_enable_fops); + if (IS_ERR(err_ptr)) { + pr_err("Error-Could not create enable file\n"); + debugfs_remove_recursive(reg_subdir); + goto error; + } + + mode = 0; + /* Force-Disable File */ + 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, reg, ®_fdisable_fops); + if (IS_ERR(err_ptr)) { + pr_err("Error-Could not create force_disable file\n"); + debugfs_remove_recursive(reg_subdir); + goto error; + } + + mode = 0; + /* Voltage File */ + 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, + reg, ®_volt_fops); + if (IS_ERR(err_ptr)) { + pr_err("Error-Could not create voltage file\n"); + debugfs_remove_recursive(reg_subdir); + goto error; + } + + mode = 0; + /* Mode File */ + 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, + reg, ®_mode_fops); + if (IS_ERR(err_ptr)) { + pr_err("Error-Could not create mode file\n"); + debugfs_remove_recursive(reg_subdir); + goto error; + } + + mode = 0; + /* Optimum Mode File */ + 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, reg, ®_optimum_mode_fops); + if (IS_ERR(err_ptr)) { + pr_err("Error-Could not create optimum_mode file\n"); + 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) +{ + return; +} + +static inline void reg_debug_init(void) +{ + return; +} +#endif + /** * regulator_register - register regulator * @regulator_desc: regulator to register @@ -2400,6 +2732,7 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, list_add(&rdev->list, ®ulator_list); out: mutex_unlock(®ulator_list_mutex); + regulator_debug_create_directory(rdev); return rdev; unset_supplies: @@ -2575,6 +2908,8 @@ static int __init regulator_init(void) regulator_dummy_init(); + reg_debug_init(); + return ret; } -- 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. -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html