regulator: debugfs: Adding debugfs functions into regulator framework
Move regulator debugfs functions into core.c. This change allows the user to read and edit regulator information in user space through the debugfs file system. Change-Id: I21fa2c1d5d4ab27871ebb543134c2cee5ad70ded Signed-off-by: Brandon Leong <bleong@codeaurora.org> [abhimany: constify reg ops] Signed-off-by: Abhimanyu Kapur <abhimany@codeaurora.org> [adharmap: resolve merge conflicts, renamed set_optimum to set_load] Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org> Conflicts: drivers/regulator/core.c
This commit is contained in:
parent
e6a67e70d1
commit
32c846e998
2 changed files with 350 additions and 1 deletions
|
@ -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>
|
||||
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue