123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (c) 2020, The Linux Foundation. All rights reserved.
- */
- #include <linux/bitops.h>
- #include <linux/debugfs.h>
- #include <linux/device.h>
- #include <linux/err.h>
- #include <linux/kernel.h>
- #include <linux/list.h>
- #include <linux/module.h>
- #include <linux/mutex.h>
- #include <linux/of.h>
- #include <linux/slab.h>
- #include <linux/string.h>
- #include <linux/regulator/consumer.h>
- #include <linux/regulator/coupler.h>
- #include <linux/regulator/driver.h>
- #include <linux/regulator/machine.h>
- #include <linux/regulator/debug-regulator.h>
- #include <trace/events/power.h>
- #include "internal.h"
- struct debug_regulator {
- struct list_head list;
- struct regulator *reg;
- struct device *dev;
- struct regulator_dev *rdev;
- };
- static DEFINE_MUTEX(debug_reg_list_lock);
- static LIST_HEAD(debug_reg_list);
- static const char *rdev_name(struct regulator_dev *rdev)
- {
- if (rdev->constraints && rdev->constraints->name)
- return rdev->constraints->name;
- else if (rdev->desc->name)
- return rdev->desc->name;
- else
- return "";
- }
- #define dreg_err(dreg, fmt, ...) \
- pr_err("%s: %s: " fmt, __func__, rdev_name((dreg)->rdev), ##__VA_ARGS__)
- #define dreg_dbg(dreg, fmt, ...) \
- pr_debug("%s: %s: " fmt, __func__, rdev_name((dreg)->rdev), \
- ##__VA_ARGS__)
- static struct regulator *reg_debug_get_consumer(struct debug_regulator *dreg)
- {
- struct regulator *regulator;
- if (dreg->reg)
- return dreg->reg;
- regulator = regulator_get(NULL, rdev_name(dreg->rdev));
- if (IS_ERR(regulator)) {
- dreg_dbg(dreg, "debug regulator get failed, ret=%ld\n",
- PTR_ERR(regulator));
- return NULL;
- }
- dreg->reg = regulator;
- return regulator;
- }
- static int reg_debug_enable_get(void *data, u64 *val)
- {
- struct debug_regulator *dreg = data;
- struct regulator *regulator = reg_debug_get_consumer(dreg);
- if (!regulator) {
- dreg_err(dreg, "debug consumer missing\n");
- return -ENODEV;
- }
- *val = regulator_is_enabled(regulator);
- return 0;
- }
- static int reg_debug_enable_set(void *data, u64 val)
- {
- struct debug_regulator *dreg = data;
- struct regulator *regulator = reg_debug_get_consumer(dreg);
- int ret;
- if (!regulator) {
- dreg_err(dreg, "debug consumer missing\n");
- return -ENODEV;
- }
- if (val) {
- ret = regulator_enable(regulator);
- if (ret)
- dreg_err(dreg, "enable failed, ret=%d\n", ret);
- } else {
- ret = regulator_disable(regulator);
- if (ret)
- dreg_err(dreg, "disable failed, ret=%d\n", ret);
- }
- return ret;
- }
- DEFINE_DEBUGFS_ATTRIBUTE(reg_enable_fops, reg_debug_enable_get,
- reg_debug_enable_set, "%llu\n");
- static int reg_debug_bypass_enable_get(void *data, u64 *val)
- {
- struct debug_regulator *dreg = data;
- struct regulator_dev *rdev = dreg->rdev;
- bool enable = false;
- int ret = 0;
- ww_mutex_lock(&rdev->mutex, NULL);
- if (rdev->desc->ops->get_bypass) {
- ret = rdev->desc->ops->get_bypass(rdev, &enable);
- if (ret)
- dreg_err(dreg, "get_bypass() failed, ret=%d\n", ret);
- } else {
- enable = (rdev->bypass_count == rdev->open_count);
- }
- ww_mutex_unlock(&rdev->mutex);
- *val = enable;
- return ret;
- }
- static int reg_debug_bypass_enable_set(void *data, u64 val)
- {
- struct debug_regulator *dreg = data;
- struct regulator *regulator = reg_debug_get_consumer(dreg);
- if (!regulator) {
- dreg_err(dreg, "debug consumer missing\n");
- return -ENODEV;
- }
- return regulator_allow_bypass(regulator, val);
- }
- DEFINE_DEBUGFS_ATTRIBUTE(reg_bypass_enable_fops, reg_debug_bypass_enable_get,
- reg_debug_bypass_enable_set, "%llu\n");
- static int reg_debug_force_disable_set(void *data, u64 val)
- {
- struct debug_regulator *dreg = data;
- struct regulator *regulator = reg_debug_get_consumer(dreg);
- int ret = 0;
- if (!regulator) {
- dreg_err(dreg, "debug consumer missing\n");
- return -ENODEV;
- }
- if (val) {
- ret = regulator_force_disable(regulator);
- if (ret)
- dreg_err(dreg, "force_disable failed, ret=%d\n", ret);
- }
- return ret;
- }
- DEFINE_DEBUGFS_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_read(struct file *file, char __user *ubuf,
- size_t count, loff_t *ppos)
- {
- struct debug_regulator *dreg = file->private_data;
- struct regulator *regulator = reg_debug_get_consumer(dreg);
- char buf[MAX_DEBUG_BUF_LEN];
- int voltage, ret;
- if (!regulator) {
- dreg_err(dreg, "debug consumer missing\n");
- return -ENODEV;
- }
- voltage = regulator_get_voltage(regulator);
- ret = scnprintf(buf, MAX_DEBUG_BUF_LEN, "%d\n", voltage);
- return simple_read_from_buffer(ubuf, count, ppos, buf, ret);
- }
- static ssize_t reg_debug_voltage_write(struct file *file,
- const char __user *ubuf, size_t count, loff_t *ppos)
- {
- struct debug_regulator *dreg = file->private_data;
- struct regulator *regulator = reg_debug_get_consumer(dreg);
- char buf[MAX_DEBUG_BUF_LEN];
- int ret, filled;
- int min_uV, max_uV = -1;
- if (!regulator) {
- dreg_err(dreg, "debug consumer missing\n");
- return -ENODEV;
- }
- if (count < MAX_DEBUG_BUF_LEN) {
- if (copy_from_user(buf, ubuf, count))
- return -EFAULT;
- buf[count] = '\0';
- filled = sscanf(buf, "%d %d", &min_uV, &max_uV);
- /* Check that both min and max voltage were specified. */
- if (filled < 2 || min_uV < 0 || max_uV < min_uV) {
- dreg_err(dreg, "incorrect values specified: \"%s\"; should be: \"min_uV max_uV\"\n",
- buf);
- return -EINVAL;
- }
- ret = regulator_set_voltage(regulator, min_uV, max_uV);
- if (ret) {
- dreg_err(dreg, "set_voltage(%d, %d) failed, ret=%d\n",
- min_uV, max_uV, ret);
- return ret;
- }
- } else {
- dreg_err(dreg, "voltage request string exceeds maximum buffer size\n");
- return -EINVAL;
- }
- return count;
- }
- static const struct file_operations reg_voltage_fops = {
- .open = simple_open,
- .read = reg_debug_voltage_read,
- .write = reg_debug_voltage_write,
- };
- static int reg_debug_mode_get(void *data, u64 *val)
- {
- struct debug_regulator *dreg = data;
- struct regulator *regulator = reg_debug_get_consumer(dreg);
- int mode;
- if (!regulator) {
- dreg_err(dreg, "debug consumer missing\n");
- return -ENODEV;
- }
- mode = regulator_get_mode(regulator);
- if (mode < 0) {
- dreg_err(dreg, "get mode failed, ret=%d\n", mode);
- return mode;
- }
- *val = mode;
- return 0;
- }
- static int reg_debug_mode_set(void *data, u64 val)
- {
- struct debug_regulator *dreg = data;
- struct regulator *regulator = reg_debug_get_consumer(dreg);
- unsigned int mode = val;
- int ret;
- if (!regulator) {
- dreg_err(dreg, "debug consumer missing\n");
- return -ENODEV;
- }
- ret = regulator_set_mode(regulator, mode);
- if (ret)
- dreg_err(dreg, "set mode=%u failed, ret=%d\n", mode, ret);
- return ret;
- }
- DEFINE_DEBUGFS_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 debug_regulator *dreg = data;
- struct regulator *regulator = reg_debug_get_consumer(dreg);
- int load = val;
- int ret;
- if (!regulator) {
- dreg_err(dreg, "debug consumer missing\n");
- return -ENODEV;
- }
- ret = regulator_set_load(regulator, load);
- if (ret)
- dreg_err(dreg, "set load=%d failed, ret=%d\n", load, ret);
- return ret;
- }
- DEFINE_DEBUGFS_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;
- const char *supply_name;
- ww_mutex_lock(&rdev->mutex, NULL);
- /* Print a header if there are consumers. */
- if (rdev->open_count)
- seq_printf(m, "%-32s EN 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 %c %8d %8d %8d\n", supply_name,
- (reg->enable_count ? 'Y' : 'N'),
- reg->voltage[PM_SUSPEND_ON].min_uV,
- reg->voltage[PM_SUSPEND_ON].max_uV,
- reg->uA_load);
- }
- ww_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 = {
- .open = reg_debug_consumers_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- };
- /**
- * regulator_debug_add() - register a debug regulator for the specified
- * regulator
- * @dev: Device pointer of the regulator
- * @rdev: Regulator dev pointer of the regulator
- *
- * This function adds various debugfs files for the 'rdev' regulator which
- * provide a mechanism for userspace to vote and monitor the state of the
- * regulator. When one of these debugfs files is accessed,
- * reg_debug_get_consumer() is called which uses regulator_get() to get a handle
- * to the regulator.
- *
- * Returns 0 on success or an errno on failure.
- */
- static struct debug_regulator *regulator_debug_add(struct device *dev,
- struct regulator_dev *rdev)
- {
- struct debug_regulator *dreg = NULL;
- const struct regulator_ops *ops;
- struct dentry *dir;
- mode_t mode;
- if (!dev || !rdev) {
- pr_err("%s: dev or rdev is NULL\n", __func__);
- return ERR_PTR(-EINVAL);
- }
- dreg = kzalloc(sizeof(*dreg), GFP_KERNEL);
- if (!dreg)
- return ERR_PTR(-ENOMEM);
- dreg->dev = dev;
- dreg->rdev = rdev;
- mutex_lock(&debug_reg_list_lock);
- list_add(&dreg->list, &debug_reg_list);
- mutex_unlock(&debug_reg_list_lock);
- ops = rdev->desc->ops;
- dir = rdev->debugfs;
- debugfs_create_file_unsafe("enable", 0644, dir, dreg, ®_enable_fops);
- if (ops->set_bypass)
- debugfs_create_file_unsafe("bypass", 0644, dir, dreg,
- ®_bypass_enable_fops);
- mode = 0;
- if (ops->is_enabled)
- mode |= 0444;
- if (ops->disable)
- mode |= 0200;
- if (mode)
- debugfs_create_file_unsafe("force_disable", mode, dir, dreg,
- ®_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_unsafe("voltage", mode, dir, dreg,
- ®_voltage_fops);
- mode = 0;
- if (ops->get_mode)
- mode |= 0444;
- if (ops->set_mode)
- mode |= 0200;
- if (mode)
- debugfs_create_file_unsafe("mode", mode, dir, dreg,
- ®_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_unsafe("load", mode, dir, dreg,
- ®_set_load_fops);
- debugfs_create_file("consumers", 0444, dir, rdev, ®_consumers_fops);
- return dreg;
- }
- /**
- * regulator_debug_register() - register a debug regulator for the specified
- * regulator
- * @dev: Device pointer of the regulator
- * @rdev: Regulator dev pointer of the regulator
- *
- * This function calls regulator_debug_add() which adds several debugfs files
- * for the 'rdev' regulator which allow for userspace regulator state voting and
- * monitoring.
- *
- * Returns 0 on success or an errno on failure.
- */
- int regulator_debug_register(struct device *dev, struct regulator_dev *rdev)
- {
- return PTR_ERR_OR_ZERO(regulator_debug_add(dev, rdev));
- }
- EXPORT_SYMBOL(regulator_debug_register);
- /* debug_reg_list_lock must be held by caller. */
- static void regulator_debug_remove(struct debug_regulator *dreg)
- {
- regulator_put(dreg->reg);
- list_del(&dreg->list);
- kfree(dreg);
- }
- /**
- * regulator_debug_unregister() - unregister the debug regulator associated with
- * a regulator
- * @rdev: Regulator dev pointer of the regulator
- *
- * This function removes the debugfs consumer registered for 'rdev' and then
- * frees the debug regulator's resources.
- */
- void regulator_debug_unregister(struct regulator_dev *rdev)
- {
- struct debug_regulator *dreg, *temp;
- if (IS_ERR_OR_NULL(rdev)) {
- pr_err("%s: invalid regulator device pointer\n", __func__);
- return;
- }
- mutex_lock(&debug_reg_list_lock);
- list_for_each_entry_safe(dreg, temp, &debug_reg_list, list) {
- if (dreg->rdev == rdev)
- regulator_debug_remove(dreg);
- }
- mutex_unlock(&debug_reg_list_lock);
- }
- EXPORT_SYMBOL(regulator_debug_unregister);
- /* debug_reg_list_lock must be held by caller. */
- static void _devm_regulator_debug_release(struct device *dev, void *res)
- {
- struct debug_regulator *dreg = *(struct debug_regulator **)res;
- struct debug_regulator *temp;
- bool found = false;
- /*
- * The debug regulator may have already been removed due to a
- * devm_regulator_debug_unregister() call. Therefore, verify that it is
- * still in the list before attempting to remove it.
- */
- list_for_each_entry(temp, &debug_reg_list, list) {
- if (temp == dreg) {
- found = true;
- break;
- }
- }
- if (found)
- regulator_debug_remove(dreg);
- }
- static void devm_regulator_debug_release(struct device *dev, void *res)
- {
- mutex_lock(&debug_reg_list_lock);
- _devm_regulator_debug_release(dev, res);
- mutex_unlock(&debug_reg_list_lock);
- }
- /**
- * devm_regulator_debug_register() - resource managed version of
- * regulator_debug_register()
- * @dev: Device pointer of the regulator
- * @rdev: Regulator dev pointer of the regulator
- *
- * This is a resource managed version of regulator_debug_register().
- * The debugfs consumer added via this call is automatically removed via
- * regulator_debug_unregister() on driver detach. See regulator_debug_register()
- * for more details.
- *
- * Returns 0 on success or an errno on failure.
- */
- int devm_regulator_debug_register(struct device *dev,
- struct regulator_dev *rdev)
- {
- struct debug_regulator *dreg;
- struct debug_regulator **ptr;
- ptr = devres_alloc(devm_regulator_debug_release, sizeof(*ptr),
- GFP_KERNEL);
- if (!ptr)
- return -ENOMEM;
- dreg = regulator_debug_add(dev, rdev);
- if (IS_ERR_OR_NULL(dreg)) {
- devres_free(ptr);
- return PTR_ERR(dreg);
- }
- *ptr = dreg;
- devres_add(dev, ptr);
- return 0;
- }
- EXPORT_SYMBOL(devm_regulator_debug_register);
- static int devm_regulator_debug_match(struct device *dev, void *res, void *data)
- {
- struct debug_regulator **dreg = res;
- if (!dreg || !*dreg) {
- WARN_ON(!dreg || !*dreg);
- return 0;
- }
- return *dreg == data;
- }
- /**
- * devm_regulator_debug_unregister() - resource managed version of
- * regulator_debug_unregister()
- * @rdev: Regulator dev pointer of the regulator
- *
- * Deallocate the debug regulator allocated for 'rdev' with
- * devm_regulator_debug_register(). Normally this function will not
- * need to be called and the resource management code will ensure that the
- * resource is freed.
- */
- void devm_regulator_debug_unregister(struct regulator_dev *rdev)
- {
- struct debug_regulator *dreg, *temp;
- if (IS_ERR_OR_NULL(rdev)) {
- pr_err("%s: invalid regulator device pointer\n", __func__);
- return;
- }
- mutex_lock(&debug_reg_list_lock);
- list_for_each_entry_safe(dreg, temp, &debug_reg_list, list) {
- if (dreg->rdev == rdev)
- devres_release(dreg->dev, _devm_regulator_debug_release,
- devm_regulator_debug_match, dreg);
- }
- mutex_unlock(&debug_reg_list_lock);
- }
- EXPORT_SYMBOL(devm_regulator_debug_unregister);
- static int _regulator_is_enabled(struct regulator_dev *rdev)
- {
- if (rdev->ena_pin)
- return rdev->ena_gpio_state;
- if (!rdev->desc->ops->is_enabled)
- return 1;
- return rdev->desc->ops->is_enabled(rdev);
- }
- static void regulator_debug_print_enabled(struct regulator_dev *rdev)
- {
- struct regulator *reg;
- const char *supply_name;
- int mode = -EPERM;
- int uV = -EPERM;
- if (_regulator_is_enabled(rdev) <= 0)
- return;
- uV = regulator_get_voltage_rdev(rdev);
- if (rdev->desc->ops->get_mode)
- mode = rdev->desc->ops->get_mode(rdev);
- if (uV != -EPERM && mode != -EPERM)
- pr_info("%s[%u] %d uV, mode=%d\n",
- rdev_name(rdev), rdev->use_count, uV, mode);
- else if (uV != -EPERM)
- pr_info("%s[%u] %d uV\n",
- rdev_name(rdev), rdev->use_count, uV);
- else if (mode != -EPERM)
- pr_info("%s[%u], mode=%d\n",
- rdev_name(rdev), rdev->use_count, mode);
- else
- pr_info("%s[%u]\n", rdev_name(rdev), rdev->use_count);
- /* Print a header if there are consumers. */
- if (rdev->open_count)
- pr_info(" %-32s EN 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)";
- pr_info(" %-32s %d %8d %8d %8d\n", supply_name,
- reg->enable_count,
- reg->voltage[PM_SUSPEND_ON].min_uV,
- reg->voltage[PM_SUSPEND_ON].max_uV,
- reg->uA_load);
- }
- }
- static void regulator_debug_suspend_trace_probe(void *unused,
- const char *action, int val, bool start)
- {
- struct debug_regulator *dreg;
- if (start && val > 0 && !strcmp("machine_suspend", action)) {
- pr_info("Enabled regulators:\n");
- list_for_each_entry(dreg, &debug_reg_list, list)
- regulator_debug_print_enabled(dreg->rdev);
- }
- }
- static bool debug_suspend;
- static struct dentry *regulator_suspend_debugfs;
- static int reg_debug_suspend_enable_get(void *data, u64 *val)
- {
- *val = debug_suspend;
- return 0;
- }
- static int reg_debug_suspend_enable_set(void *data, u64 val)
- {
- int ret;
- val = !!val;
- if (val == debug_suspend)
- return 0;
- if (val)
- ret = register_trace_suspend_resume(
- regulator_debug_suspend_trace_probe, NULL);
- else
- ret = unregister_trace_suspend_resume(
- regulator_debug_suspend_trace_probe, NULL);
- if (ret) {
- pr_err("%s: Failed to %sregister suspend trace callback, ret=%d\n",
- __func__, val ? "" : "un", ret);
- return ret;
- }
- debug_suspend = val;
- return 0;
- }
- DEFINE_DEBUGFS_ATTRIBUTE(reg_debug_suspend_enable_fops,
- reg_debug_suspend_enable_get, reg_debug_suspend_enable_set, "%llu\n");
- static int __init regulator_debug_init(void)
- {
- static struct dentry *dir;
- int ret;
- dir = debugfs_lookup("regulator", NULL);
- if (IS_ERR_OR_NULL(dir)) {
- ret = PTR_ERR(dir);
- pr_err("%s: unable to find root regulator debugfs directory, ret=%d\n",
- __func__, ret);
- return 0;
- }
- regulator_suspend_debugfs = debugfs_create_file_unsafe("debug_suspend",
- 0644, dir, NULL,
- ®_debug_suspend_enable_fops);
- dput(dir);
- if (IS_ERR(regulator_suspend_debugfs)) {
- ret = PTR_ERR(regulator_suspend_debugfs);
- pr_err("%s: unable to create regulator debug_suspend debugfs directory, ret=%d\n",
- __func__, ret);
- }
- return 0;
- }
- module_init(regulator_debug_init);
- static void __exit regulator_debug_exit(void)
- {
- debugfs_remove(regulator_suspend_debugfs);
- if (debug_suspend)
- unregister_trace_suspend_resume(
- regulator_debug_suspend_trace_probe, NULL);
- }
- module_exit(regulator_debug_exit);
- MODULE_DESCRIPTION("Regulator debug control library");
- MODULE_LICENSE("GPL v2");
|