regulator: debugfs: Adding debugfs functions into regulator framework

This change allows the user to read and edit regulator information
in user space through the debugfs file system.

Base on msm work.

Change-Id: I4b40d4fd662e3d3d0856127e8e030fa60e938df9
Signed-off-by: Tao Huang <huangtao@rock-chips.com>
Signed-off-by: Finley Xiao <finley.xiao@rock-chips.com>
This commit is contained in:
Tao Huang
2017-12-27 18:57:36 +08:00
parent 6cd300f795
commit 707eebb0c9
2 changed files with 299 additions and 1 deletions

View File

@@ -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>
@@ -3860,11 +3862,243 @@ 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_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;
struct regulator_dev *rdev = regulator->rdev;
struct regulator *reg;
char buf[MAX_DEBUG_BUF_LEN];
int ret;
int min_uV, max_uV = -1;
if (count < MAX_DEBUG_BUF_LEN) {
if (copy_from_user(buf, ubuf, count))
return -EFAULT;
buf[count] = '\0';
ret = kstrtoint(buf, 10, &min_uV);
/* Check that target voltage were specified. */
if (ret || min_uV < 0) {
rdev_err(regulator->rdev, "incorrect values specified: \"%s\"; should be: \"target_uV\"\n",
buf);
return -EINVAL;
}
max_uV = rdev->constraints->max_uV;
list_for_each_entry(reg, &rdev->consumer_list, list) {
if ((!reg->min_uV && !reg->max_uV) ||
(reg == regulator))
continue;
reg->min_uV = min_uV;
reg->max_uV = max_uV;
}
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 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 %8d %8d %8d\n", supply_name,
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) {
@@ -3885,8 +4119,71 @@ 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;
ops = rdev->desc->ops;
debugfs_create_file("enable", 0644, rdev->debugfs, regulator,
&reg_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);
@@ -4093,8 +4390,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);

View File

@@ -414,6 +414,7 @@ struct regulator_dev {
/* time when this regulator was disabled last time */
unsigned long last_off_jiffy;
struct regulator *debug_consumer;
};
struct regulator_dev *