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
Signed-off-by: Heiko Stuebner <heiko@...>
drivers/regulator/core.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 113 insertions(+)
@@ -3936,6 +3936,116 @@ 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, "%8dmV ",
+ _regulator_get_voltage(rdev) / 1000);
+ break;
+ case REGULATOR_CURRENT:
+ seq_printf(s, "%8dmA ",
+ _regulator_get_current_limit(rdev) / 1000);
+ break;
+ }
+
+ c = rdev->constraints;
+ if (c) {
+ switch (rdev->desc->type) {
+ case REGULATOR_VOLTAGE:
+ seq_printf(s, "%8dmV %8dmV ",
+ c->min_uV / 1000, c->max_uV / 1000);
+ break;
+ case REGULATOR_CURRENT:
+ seq_printf(s, "%8dmA %8dmA ",
+ 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 == ®ulator_class)
+ continue;
+
+ seq_printf(s, "%*s%-*s ",
+ (level + 1) * 3 + 1, "",
+ 30 - (level + 1) * 3, dev_name(consumer->dev));
+
+ if (rdev->desc->type == REGULATOR_VOLTAGE)
+ seq_printf(s, "%35dmV %8dmV",
+ consumer->min_uV / 1000,
+ consumer->max_uV / 1000);
+
+ seq_puts(s, "\n");
+ }
+
+ mutex_unlock(&rdev->mutex);
+
+ list_for_each_entry(child, list, list) {
+ 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(®ulator_list_mutex);
+
+ list_for_each_entry(rdev, list, list) {
+ if (rdev->supply)
+ continue;
+
+ regulator_summary_show_subtree(s, rdev, 0);
+ }
+
+ mutex_unlock(®ulator_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;
@@ -3949,6 +4059,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,
+ ®ulator_list, ®ulator_summary_fops);
+
regulator_dummy_init();
return ret;