summaryrefslogtreecommitdiff
path: root/drivers/regulator
diff options
context:
space:
mode:
authorHeiko Stübner <heiko@sntech.de>2015-04-07 17:16:39 +0300
committerMark Brown <broonie@kernel.org>2015-04-10 17:46:28 +0300
commit7c225ec90c368a474daa9803922f4b7d6fe6d5c8 (patch)
tree0c42e3095d5c14b8707a1bed1d12cc9aec1c73a7 /drivers/regulator
parenta9eaa8130707d4013fb109b80323489c0d0111ac (diff)
downloadlinux-7c225ec90c368a474daa9803922f4b7d6fe6d5c8.tar.xz
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>
Diffstat (limited to 'drivers/regulator')
-rw-r--r--drivers/regulator/core.c121
1 files changed, 121 insertions, 0 deletions
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index cc242aa368ef..5aae1bd61151 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -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;