diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 566518eba799..78a3a1e282a0 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #include #include @@ -2175,7 +2177,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) @@ -2287,6 +2293,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) @@ -3863,11 +3871,257 @@ static struct class regulator_class = { .dev_groups = regulator_dev_groups, }; +#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_set_load(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_load(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_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; + + if (!rdev) { + pr_err("regulator device missing"); + return -EINVAL; + } + + mutex_lock(&rdev->mutex); + + /* Print a header if there are consumers. */ + if (rdev->open_count) + seq_printf(m, "Device-Supply " + "EN Min_uV Max_uV load_uA\n"); + + 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_init_debugfs(struct regulator_dev *rdev) { struct device *parent = rdev->dev.parent; const char *rname = rdev_get_name(rdev); char name[NAME_MAX]; + struct dentry *err_ptr = NULL; + struct regulator *reg; + const struct regulator_ops *reg_ops; + mode_t mode; + + if (IS_ERR(rdev) || rdev == NULL || + IS_ERR(debugfs_root) || debugfs_root == NULL) { + pr_err("Error-Bad Function Input\n"); + goto error; + } /* Avoid duplicate debugfs directory names */ if (parent && rname == rdev->desc->name) { @@ -3879,7 +4133,8 @@ static void rdev_init_debugfs(struct regulator_dev *rdev) rdev->debugfs = debugfs_create_dir(rname, debugfs_root); if (!rdev->debugfs) { rdev_warn(rdev, "Failed to create debugfs directory\n"); - return; + rdev->debugfs = NULL; + goto error; } debugfs_create_u32("use_count", 0444, rdev->debugfs, @@ -3888,7 +4143,96 @@ 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, + ®_consumers_fops); + + reg = regulator_get(NULL, rdev->desc->name); + if (IS_ERR(reg) || reg == NULL) { + pr_err("Error-Bad Function Input\n"); + goto error; + } + + reg_ops = rdev->desc->ops; + mode = S_IRUGO | S_IWUSR; + /* Enabled File */ + if (mode) + err_ptr = debugfs_create_file("enable", mode, rdev->debugfs, + reg, ®_enable_fops); + if (IS_ERR(err_ptr)) { + pr_err("Error-Could not create enable file\n"); + debugfs_remove_recursive(rdev->debugfs); + 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, + rdev->debugfs, reg, ®_fdisable_fops); + if (IS_ERR(err_ptr)) { + pr_err("Error-Could not create force_disable file\n"); + debugfs_remove_recursive(rdev->debugfs); + 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, rdev->debugfs, + reg, ®_volt_fops); + if (IS_ERR(err_ptr)) { + pr_err("Error-Could not create voltage file\n"); + debugfs_remove_recursive(rdev->debugfs); + 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, rdev->debugfs, + reg, ®_mode_fops); + if (IS_ERR(err_ptr)) { + pr_err("Error-Could not create mode file\n"); + debugfs_remove_recursive(rdev->debugfs); + 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("load", mode, + rdev->debugfs, reg, ®_set_load_fops); + if (IS_ERR(err_ptr)) { + pr_err("Error-Could not create optimum_mode file\n"); + debugfs_remove_recursive(rdev->debugfs); + goto error; + } + +error: + return; } +#else +static inline void rdev_init_debugfs(struct regulator_dev *rdev) +{ + return; +} +#endif /** * regulator_register - register regulator @@ -4035,7 +4379,11 @@ regulator_register(const struct regulator_desc *regulator_desc, } } + mutex_unlock(®ulator_list_mutex); rdev_init_debugfs(rdev); + kfree(config); + return rdev; + out: mutex_unlock(®ulator_list_mutex); kfree(config); diff --git a/drivers/regulator/internal.h b/drivers/regulator/internal.h index c74ac8734023..99c68ab4c0d9 100644 --- a/drivers/regulator/internal.h +++ b/drivers/regulator/internal.h @@ -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;