regulator: add a summary tree in debugfs

On modern systems the regulator hierarchy can get quite long and nested
with regulators supplying other regulators. In some cases when debugging
it might be nice to get a tree of these regulators, their consumers
and the regulation constraints in one go.

To achieve this add a regulator_summary sysfs node, similar to
clk_summary in the common clock framework, that walks the regulator
list and creates a tree out of the regulators, their consumers and
core per-regulator settings.

On a rk3288-firefly the regulator_summary would for example look
something like:

 regulator                      use open bypass   value     min     max
-----------------------------------------------------------------------
 vcc_sys                          0   12      0  5000mV  5000mV  5000mV
    vcc_lan                       1    1      0  3300mV  3300mV  3300mV
       ff290000.ethernet                                    0mV     0mV
    vcca_33                       0    0      0  3300mV  3300mV  3300mV
    vcca_18                       0    0      0  1800mV  1800mV  1800mV
    vdd10_lcd                     0    0      0  1000mV  1000mV  1000mV
    vccio_sd                      0    0      0  3300mV  3300mV  3300mV
    vcc_20                        0    3      0  2000mV  2000mV  2000mV
       vcc18_lcd                  0    0      0  1800mV  1800mV  1800mV
       vcc_18                     0    2      0  1800mV  1800mV  1800mV
          ff100000.saradc                                   0mV     0mV
          ff0d0000.dwmmc                                 1650mV  1950mV
       vdd_10                     0    0      0  1000mV  1000mV  1000mV
    vdd_log                       0    0      0  1100mV  1100mV  1100mV
    vcc_io                        0    3      0  3300mV  3300mV  3300mV
       ff0f0000.dwmmc                                    3300mV  3400mV
       vcc_flash                  1    1      0  1800mV  1800mV  1800mV
          ff0f0000.dwmmc                                 1700mV  1950mV
       vcc_sd                     1    1      0  3300mV  3300mV  3300mV
          ff0c0000.dwmmc                                 3300mV  3400mV
    vcc_ddr                       0    0      0  1200mV  1200mV  1200mV
    vdd_gpu                       0    0      0  1000mV   850mV  1350mV
    vdd_cpu                       0    1      0   900mV   850mV  1350mV
       cpu0                                               900mV   900mV
    vcc_5v                        0    2      0  5000mV  5000mV  5000mV
       vcc_otg_5v                 0    0      0  5000mV  5000mV  5000mV
       vcc_host_5v                0    0      0  5000mV  5000mV  5000mV
 regulator-dummy                  0    0      0     0mV     0mV     0mV

Signed-off-by: Heiko Stuebner <heiko@sntech.de>
Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Heiko Stübner 2015-04-07 16:16:39 +02:00 committed by Mark Brown
parent a9eaa81307
commit 7c225ec90c
1 changed files with 121 additions and 0 deletions

View File

@ -3954,6 +3954,124 @@ static const struct file_operations supply_map_fops = {
#endif
};
#ifdef CONFIG_DEBUG_FS
static void regulator_summary_show_subtree(struct seq_file *s,
struct regulator_dev *rdev,
int level)
{
struct list_head *list = s->private;
struct regulator_dev *child;
struct regulation_constraints *c;
struct regulator *consumer;
if (!rdev)
return;
mutex_lock(&rdev->mutex);
seq_printf(s, "%*s%-*s %3d %4d %6d ",
level * 3 + 1, "",
30 - level * 3, rdev_get_name(rdev),
rdev->use_count, rdev->open_count, rdev->bypass_count);
switch (rdev->desc->type) {
case REGULATOR_VOLTAGE:
seq_printf(s, "%5dmV ",
_regulator_get_voltage(rdev) / 1000);
break;
case REGULATOR_CURRENT:
seq_printf(s, "%5dmA ",
_regulator_get_current_limit(rdev) / 1000);
break;
}
c = rdev->constraints;
if (c) {
switch (rdev->desc->type) {
case REGULATOR_VOLTAGE:
seq_printf(s, "%5dmV %5dmV ",
c->min_uV / 1000, c->max_uV / 1000);
break;
case REGULATOR_CURRENT:
seq_printf(s, "%5dmA %5dmA ",
c->min_uA / 1000, c->max_uA / 1000);
break;
}
}
seq_puts(s, "\n");
list_for_each_entry(consumer, &rdev->consumer_list, list) {
if (consumer->dev->class == &regulator_class)
continue;
seq_printf(s, "%*s%-*s ",
(level + 1) * 3 + 1, "",
30 - (level + 1) * 3, dev_name(consumer->dev));
switch (rdev->desc->type) {
case REGULATOR_VOLTAGE:
seq_printf(s, "%29dmV %5dmV",
consumer->min_uV / 1000,
consumer->max_uV / 1000);
break;
case REGULATOR_CURRENT:
seq_printf(s, "%37dmA",
regulator_get_current_limit(consumer) / 1000);
break;
}
seq_puts(s, "\n");
}
mutex_unlock(&rdev->mutex);
list_for_each_entry(child, list, list) {
/* handle only non-root regulators supplied by current rdev */
if (!child->supply || child->supply->rdev != rdev)
continue;
regulator_summary_show_subtree(s, child, level + 1);
}
}
static int regulator_summary_show(struct seq_file *s, void *data)
{
struct list_head *list = s->private;
struct regulator_dev *rdev;
seq_puts(s, " regulator use open bypass value min max\n");
seq_puts(s, "-----------------------------------------------------------------------\n");
mutex_lock(&regulator_list_mutex);
list_for_each_entry(rdev, list, list) {
if (rdev->supply)
continue;
regulator_summary_show_subtree(s, rdev, 0);
}
mutex_unlock(&regulator_list_mutex);
return 0;
}
static int regulator_summary_open(struct inode *inode, struct file *file)
{
return single_open(file, regulator_summary_show, inode->i_private);
}
#endif
static const struct file_operations regulator_summary_fops = {
#ifdef CONFIG_DEBUG_FS
.open = regulator_summary_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
#endif
};
static int __init regulator_init(void)
{
int ret;
@ -3967,6 +4085,9 @@ static int __init regulator_init(void)
debugfs_create_file("supply_map", 0444, debugfs_root, NULL,
&supply_map_fops);
debugfs_create_file("regulator_summary", 0444, debugfs_root,
&regulator_list, &regulator_summary_fops);
regulator_dummy_init();
return ret;