Loading arch/arm64/configs/vendor/lahaina_GKI.config +2 −0 Original line number Diff line number Diff line CONFIG_ARCH_QCOM=y CONFIG_ARCH_LAHAINA=y CONFIG_REGULATOR_STUB=m CONFIG_REGULATOR_PROXY_CONSUMER=m CONFIG_PINCTRL_LAHAINA=m CONFIG_EXTCON=m CONFIG_NOP_USB_XCEIV=m Loading Loading @@ -46,6 +47,7 @@ CONFIG_MSM_SERVICE_NOTIFIER=m CONFIG_QSEE_IPC_IRQ=m CONFIG_RPMSG_QCOM_GLINK_SPSS=m CONFIG_REGULATOR_RPMH=m CONFIG_REGULATOR_FIXED_VOLTAGE=m CONFIG_MSM_GCC_LAHAINA=m CONFIG_REGULATOR_REFGEN=m CONFIG_MSM_VIDEOCC_LAHAINA=m Loading drivers/regulator/Kconfig +21 −0 Original line number Diff line number Diff line Loading @@ -65,6 +65,27 @@ config REGULATOR_USERSPACE_CONSUMER If unsure, say no. config REGULATOR_PROXY_CONSUMER tristate "Boot time regulator proxy consumer support" help This library provides support for boot time regulator proxy requests. It can enforce a specified voltage range, set a minimum current, and/or keep a regulator enabled. It is needed in circumstances where reducing one or more of these three quantities will cause hardware to stop working if performed before the driver managing the hardware has probed. config REGULATOR_PROXY_CONSUMER_LEGACY bool "Legacy proxy consumer unvoting support" depends on REGULATOR_PROXY_CONSUMER help By default, the proxy consumer library unvotes the proxy requests made for a given device when the sync_state() callback of the device is called after all of its consumers have probed. When this legacy unvoting feature is enabled, unvoting happens unconditionally at late_initcall_sync() instead of per-device via sync_state() callback calls. config REGULATOR_88PG86X tristate "Marvell 88PG86X voltage regulators" depends on I2C Loading drivers/regulator/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -9,6 +9,7 @@ obj-$(CONFIG_OF) += of_regulator.o obj-$(CONFIG_REGULATOR_FIXED_VOLTAGE) += fixed.o obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER) += userspace-consumer.o obj-$(CONFIG_REGULATOR_PROXY_CONSUMER) += proxy-consumer.o obj-$(CONFIG_REGULATOR_88PG86X) += 88pg86x.o obj-$(CONFIG_REGULATOR_88PM800) += 88pm800-regulator.o Loading drivers/regulator/fixed.c +7 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ #include <linux/of.h> #include <linux/of_device.h> #include <linux/regulator/of_regulator.h> #include <linux/regulator/proxy-consumer.h> #include <linux/regulator/machine.h> #include <linux/clk.h> Loading Loading @@ -249,6 +250,11 @@ static int reg_fixed_voltage_probe(struct platform_device *pdev) return ret; } ret = devm_regulator_proxy_consumer_register(dev, dev->of_node); if (ret) dev_err(dev, "failed to register proxy consumer, ret=%d\n", ret); platform_set_drvdata(pdev, drvdata); dev_dbg(&pdev->dev, "%s supplying %duV\n", drvdata->desc.name, Loading Loading @@ -278,6 +284,7 @@ static struct platform_driver regulator_fixed_voltage_driver = { .driver = { .name = "reg-fixed-voltage", .of_match_table = of_match_ptr(fixed_of_match), .sync_state = regulator_proxy_consumer_sync_state, }, }; Loading drivers/regulator/proxy-consumer.c 0 → 100644 +399 −0 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2019, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "%s: " fmt, __func__ #include <linux/bitops.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/regulator/consumer.h> #include <linux/regulator/proxy-consumer.h> struct proxy_consumer { struct list_head list; struct regulator *reg; struct device *dev; bool enable; int min_uV; int max_uV; u32 current_uA; }; static DEFINE_MUTEX(proxy_consumer_list_lock); static LIST_HEAD(proxy_consumer_list); static bool proxy_consumers_removed; /** * regulator_proxy_consumer_add() - conditionally add a proxy consumer for the * specified regulator and set its boot time * parameters * @dev: Device pointer of the regulator * @node: Device node pointer of the regulator * * This function calls regulator_get() after first checking if any proxy * consumer properties are present in the 'node' device node. After that, the * voltage, minimum current, and/or the enable state will be set based upon the * device node property values. * * Returns a valid pointer on successfully proxy voting, NULL if no proxy voting * is needed, or an ERR_PTR(errno) if an error occurred. */ static struct proxy_consumer *regulator_proxy_consumer_add(struct device *dev, struct device_node *node) { struct proxy_consumer *consumer = NULL; const char *reg_name = ""; const char *supply_name; u32 voltage[2] = {0}; int ret; if (!dev || !node) { pr_err("dev or node is NULL\n"); return ERR_PTR(-EINVAL); } /* Return immediately if no proxy consumer properties are specified. */ if (!of_find_property(node, "qcom,proxy-consumer-enable", NULL) && !of_find_property(node, "qcom,proxy-consumer-voltage", NULL) && !of_find_property(node, "qcom,proxy-consumer-current", NULL)) return NULL; mutex_lock(&proxy_consumer_list_lock); /* Do not register new consumers if they cannot be removed later. */ if (proxy_consumers_removed) { ret = -EPERM; goto unlock_list; } if (node->name) reg_name = node->name; consumer = kzalloc(sizeof(*consumer), GFP_KERNEL); if (!consumer) { ret = -ENOMEM; goto unlock_list; } consumer->dev = dev; consumer->enable = of_property_read_bool(node, "qcom,proxy-consumer-enable"); of_property_read_u32(node, "qcom,proxy-consumer-current", &consumer->current_uA); ret = of_property_read_u32_array(node, "qcom,proxy-consumer-voltage", voltage, 2); if (!ret) { consumer->min_uV = voltage[0]; consumer->max_uV = voltage[1]; } dev_dbg(dev, "proxy consumer request: enable=%d, voltage_range=[%d, %d] uV, min_current=%d uA\n", consumer->enable, consumer->min_uV, consumer->max_uV, consumer->current_uA); supply_name = "proxy"; of_property_read_string(node, "qcom,proxy-consumer-name", &supply_name); consumer->reg = regulator_get(dev, supply_name); if (IS_ERR_OR_NULL(consumer->reg)) { ret = PTR_ERR(consumer->reg); pr_err("regulator_get(%s) failed for %s, ret=%d\n", supply_name, reg_name, ret); goto free_consumer; } if (consumer->max_uV > 0 && consumer->min_uV <= consumer->max_uV) { ret = regulator_set_voltage(consumer->reg, consumer->min_uV, consumer->max_uV); if (ret) { pr_err("regulator_set_voltage %s failed, ret=%d\n", reg_name, ret); goto free_regulator; } } if (consumer->current_uA > 0) { ret = regulator_set_load(consumer->reg, consumer->current_uA); if (ret < 0) { pr_err("regulator_set_load %s failed, ret=%d\n", reg_name, ret); goto remove_voltage; } } if (consumer->enable) { ret = regulator_enable(consumer->reg); if (ret) { pr_err("regulator_enable %s failed, ret=%d\n", reg_name, ret); goto remove_current; } } list_add(&consumer->list, &proxy_consumer_list); mutex_unlock(&proxy_consumer_list_lock); return consumer; remove_current: regulator_set_load(consumer->reg, 0); remove_voltage: regulator_set_voltage(consumer->reg, 0, INT_MAX); free_regulator: regulator_put(consumer->reg); free_consumer: kfree(consumer); unlock_list: mutex_unlock(&proxy_consumer_list_lock); return ERR_PTR(ret); } /** * regulator_proxy_consumer_register() - conditionally register a proxy consumer * for the specified regulator and set its boot time parameters * @dev: Device pointer of the regulator * @node: Device node pointer of the regulator * * This function calls regulator_get() after first checking if any proxy * consumer properties are present in the 'node' device node. After that, the * voltage, minimum current, and/or the enable state will be set based upon the * device node property values. * * Returns 0 on successfully proxy voting or if no proxy voting is needed, or an * errno if an error occurred. */ int regulator_proxy_consumer_register(struct device *dev, struct device_node *node) { struct proxy_consumer *consumer; consumer = regulator_proxy_consumer_add(dev, node); return PTR_ERR_OR_ZERO(consumer); } EXPORT_SYMBOL(regulator_proxy_consumer_register); /* proxy_consumer_list_lock must be held by caller. */ static int regulator_proxy_consumer_remove(struct proxy_consumer *consumer) { int ret = 0; if (consumer->enable) { ret = regulator_disable(consumer->reg); if (ret) pr_err("regulator_disable failed, ret=%d\n", ret); } if (consumer->current_uA > 0) { ret = regulator_set_load(consumer->reg, 0); if (ret < 0) pr_err("regulator_set_load failed, ret=%d\n", ret); } if (consumer->max_uV > 0 && consumer->min_uV <= consumer->max_uV) { ret = regulator_set_voltage(consumer->reg, 0, INT_MAX); if (ret) pr_err("regulator_set_voltage failed, ret=%d\n", ret); } regulator_put(consumer->reg); list_del(&consumer->list); kfree(consumer); return ret; } /** * regulator_proxy_consumer_unregister() - unregister the proxy consumers of a * device and remove their boot time * requests * @dev: Device pointer of the regulator * * This function removes all requests made by the proxy consumers of regulators * in dev which where issued in regulator_proxy_consumer_register() and then * frees the consumers' resources. * * Returns 0 on success or an errno on failure. */ void regulator_proxy_consumer_unregister(struct device *dev) { struct proxy_consumer *consumer, *temp; if (IS_ERR_OR_NULL(dev)) { pr_err("invalid device pointer\n"); return; } mutex_lock(&proxy_consumer_list_lock); list_for_each_entry_safe(consumer, temp, &proxy_consumer_list, list) { if (consumer->dev == dev) regulator_proxy_consumer_remove(consumer); } mutex_unlock(&proxy_consumer_list_lock); } EXPORT_SYMBOL(regulator_proxy_consumer_unregister); /* proxy_consumer_list_lock must be held by caller. */ static void _devm_regulator_proxy_consumer_release(struct device *dev, void *res) { struct proxy_consumer *consumer = *(struct proxy_consumer **)res; struct proxy_consumer *temp; bool found = false; /* * The proxy consumer may have already been removed due to a * sync_state() or devm_regulator_proxy_consumer_unregister() call. * Therefore, verify that it is still in the list before attempting to * remove it. */ list_for_each_entry(temp, &proxy_consumer_list, list) { if (temp == consumer) { found = true; break; } } if (found) regulator_proxy_consumer_remove(consumer); } static void devm_regulator_proxy_consumer_release(struct device *dev, void *res) { mutex_lock(&proxy_consumer_list_lock); _devm_regulator_proxy_consumer_release(dev, res); mutex_unlock(&proxy_consumer_list_lock); } /** * devm_regulator_proxy_consumer_register() - resource managed version of * regulator_proxy_consumer_register() * @dev: Device pointer of the regulator * @node: Device node pointer of the regulator * * This is a resource managed version of regulator_proxy_consumer_register(). * Proxy consumer requests made via this call are automatically removed via * regulator_proxy_consumer_unregister() on driver detach. See * regulator_proxy_consumer_register() for more details. * * Returns 0 on success or an errno on failure. */ int devm_regulator_proxy_consumer_register(struct device *dev, struct device_node *node) { struct proxy_consumer *consumer; struct proxy_consumer **ptr; ptr = devres_alloc(devm_regulator_proxy_consumer_release, sizeof(*ptr), GFP_KERNEL); if (!ptr) return -ENOMEM; consumer = regulator_proxy_consumer_add(dev, node); if (IS_ERR_OR_NULL(consumer)) { devres_free(ptr); return PTR_ERR(consumer); } *ptr = consumer; devres_add(dev, ptr); return 0; } EXPORT_SYMBOL(devm_regulator_proxy_consumer_register); static int devm_regulator_proxy_consumer_match(struct device *dev, void *res, void *data) { struct proxy_consumer **consumer = res; if (!consumer || !*consumer) { WARN_ON(!consumer || !*consumer); return 0; } return *consumer == data; } /** * devm_regulator_proxy_consumer_unregister() - resource managed version of * regulator_proxy_consumer_unregister() * @dev: Device pointer of the regulator * * Deallocate the proxy consumers allocated for 'dev' with * devm_regulator_proxy_consumer_register(). Normally this function will not * need to be called and the resource management code will ensure that the * resource is freed. * * Returns 0 on success or an errno on failure. */ void devm_regulator_proxy_consumer_unregister(struct device *dev) { struct proxy_consumer *consumer, *temp; if (IS_ERR_OR_NULL(dev)) return; mutex_lock(&proxy_consumer_list_lock); list_for_each_entry_safe(consumer, temp, &proxy_consumer_list, list) { if (consumer->dev == dev) devres_release(dev, _devm_regulator_proxy_consumer_release, devm_regulator_proxy_consumer_match, consumer); } mutex_unlock(&proxy_consumer_list_lock); } EXPORT_SYMBOL(devm_regulator_proxy_consumer_unregister); #ifndef CONFIG_REGULATOR_PROXY_CONSUMER_LEGACY void regulator_proxy_consumer_sync_state(struct device *dev) { regulator_proxy_consumer_unregister(dev); } EXPORT_SYMBOL(regulator_proxy_consumer_sync_state); #else /* CONFIG_REGULATOR_PROXY_CONSUMER_LEGACY=y */ void regulator_proxy_consumer_sync_state(struct device *dev) { } EXPORT_SYMBOL(regulator_proxy_consumer_sync_state); /* * Remove all proxy requests at late_initcall_sync. The assumption is that all * devices have probed at this point and made their own regulator requests. */ static int __init regulator_proxy_consumer_remove_all(void) { struct proxy_consumer *consumer; struct proxy_consumer *temp; mutex_lock(&proxy_consumer_list_lock); proxy_consumers_removed = true; if (!list_empty(&proxy_consumer_list)) pr_info("removing regulator proxy consumer requests\n"); list_for_each_entry_safe(consumer, temp, &proxy_consumer_list, list) { regulator_proxy_consumer_remove(consumer); } mutex_unlock(&proxy_consumer_list_lock); return 0; } late_initcall_sync(regulator_proxy_consumer_remove_all); #endif MODULE_DESCRIPTION("Regulator proxy consumer library"); MODULE_LICENSE("GPL v2"); Loading
arch/arm64/configs/vendor/lahaina_GKI.config +2 −0 Original line number Diff line number Diff line CONFIG_ARCH_QCOM=y CONFIG_ARCH_LAHAINA=y CONFIG_REGULATOR_STUB=m CONFIG_REGULATOR_PROXY_CONSUMER=m CONFIG_PINCTRL_LAHAINA=m CONFIG_EXTCON=m CONFIG_NOP_USB_XCEIV=m Loading Loading @@ -46,6 +47,7 @@ CONFIG_MSM_SERVICE_NOTIFIER=m CONFIG_QSEE_IPC_IRQ=m CONFIG_RPMSG_QCOM_GLINK_SPSS=m CONFIG_REGULATOR_RPMH=m CONFIG_REGULATOR_FIXED_VOLTAGE=m CONFIG_MSM_GCC_LAHAINA=m CONFIG_REGULATOR_REFGEN=m CONFIG_MSM_VIDEOCC_LAHAINA=m Loading
drivers/regulator/Kconfig +21 −0 Original line number Diff line number Diff line Loading @@ -65,6 +65,27 @@ config REGULATOR_USERSPACE_CONSUMER If unsure, say no. config REGULATOR_PROXY_CONSUMER tristate "Boot time regulator proxy consumer support" help This library provides support for boot time regulator proxy requests. It can enforce a specified voltage range, set a minimum current, and/or keep a regulator enabled. It is needed in circumstances where reducing one or more of these three quantities will cause hardware to stop working if performed before the driver managing the hardware has probed. config REGULATOR_PROXY_CONSUMER_LEGACY bool "Legacy proxy consumer unvoting support" depends on REGULATOR_PROXY_CONSUMER help By default, the proxy consumer library unvotes the proxy requests made for a given device when the sync_state() callback of the device is called after all of its consumers have probed. When this legacy unvoting feature is enabled, unvoting happens unconditionally at late_initcall_sync() instead of per-device via sync_state() callback calls. config REGULATOR_88PG86X tristate "Marvell 88PG86X voltage regulators" depends on I2C Loading
drivers/regulator/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -9,6 +9,7 @@ obj-$(CONFIG_OF) += of_regulator.o obj-$(CONFIG_REGULATOR_FIXED_VOLTAGE) += fixed.o obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER) += userspace-consumer.o obj-$(CONFIG_REGULATOR_PROXY_CONSUMER) += proxy-consumer.o obj-$(CONFIG_REGULATOR_88PG86X) += 88pg86x.o obj-$(CONFIG_REGULATOR_88PM800) += 88pm800-regulator.o Loading
drivers/regulator/fixed.c +7 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ #include <linux/of.h> #include <linux/of_device.h> #include <linux/regulator/of_regulator.h> #include <linux/regulator/proxy-consumer.h> #include <linux/regulator/machine.h> #include <linux/clk.h> Loading Loading @@ -249,6 +250,11 @@ static int reg_fixed_voltage_probe(struct platform_device *pdev) return ret; } ret = devm_regulator_proxy_consumer_register(dev, dev->of_node); if (ret) dev_err(dev, "failed to register proxy consumer, ret=%d\n", ret); platform_set_drvdata(pdev, drvdata); dev_dbg(&pdev->dev, "%s supplying %duV\n", drvdata->desc.name, Loading Loading @@ -278,6 +284,7 @@ static struct platform_driver regulator_fixed_voltage_driver = { .driver = { .name = "reg-fixed-voltage", .of_match_table = of_match_ptr(fixed_of_match), .sync_state = regulator_proxy_consumer_sync_state, }, }; Loading
drivers/regulator/proxy-consumer.c 0 → 100644 +399 −0 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2019, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "%s: " fmt, __func__ #include <linux/bitops.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/regulator/consumer.h> #include <linux/regulator/proxy-consumer.h> struct proxy_consumer { struct list_head list; struct regulator *reg; struct device *dev; bool enable; int min_uV; int max_uV; u32 current_uA; }; static DEFINE_MUTEX(proxy_consumer_list_lock); static LIST_HEAD(proxy_consumer_list); static bool proxy_consumers_removed; /** * regulator_proxy_consumer_add() - conditionally add a proxy consumer for the * specified regulator and set its boot time * parameters * @dev: Device pointer of the regulator * @node: Device node pointer of the regulator * * This function calls regulator_get() after first checking if any proxy * consumer properties are present in the 'node' device node. After that, the * voltage, minimum current, and/or the enable state will be set based upon the * device node property values. * * Returns a valid pointer on successfully proxy voting, NULL if no proxy voting * is needed, or an ERR_PTR(errno) if an error occurred. */ static struct proxy_consumer *regulator_proxy_consumer_add(struct device *dev, struct device_node *node) { struct proxy_consumer *consumer = NULL; const char *reg_name = ""; const char *supply_name; u32 voltage[2] = {0}; int ret; if (!dev || !node) { pr_err("dev or node is NULL\n"); return ERR_PTR(-EINVAL); } /* Return immediately if no proxy consumer properties are specified. */ if (!of_find_property(node, "qcom,proxy-consumer-enable", NULL) && !of_find_property(node, "qcom,proxy-consumer-voltage", NULL) && !of_find_property(node, "qcom,proxy-consumer-current", NULL)) return NULL; mutex_lock(&proxy_consumer_list_lock); /* Do not register new consumers if they cannot be removed later. */ if (proxy_consumers_removed) { ret = -EPERM; goto unlock_list; } if (node->name) reg_name = node->name; consumer = kzalloc(sizeof(*consumer), GFP_KERNEL); if (!consumer) { ret = -ENOMEM; goto unlock_list; } consumer->dev = dev; consumer->enable = of_property_read_bool(node, "qcom,proxy-consumer-enable"); of_property_read_u32(node, "qcom,proxy-consumer-current", &consumer->current_uA); ret = of_property_read_u32_array(node, "qcom,proxy-consumer-voltage", voltage, 2); if (!ret) { consumer->min_uV = voltage[0]; consumer->max_uV = voltage[1]; } dev_dbg(dev, "proxy consumer request: enable=%d, voltage_range=[%d, %d] uV, min_current=%d uA\n", consumer->enable, consumer->min_uV, consumer->max_uV, consumer->current_uA); supply_name = "proxy"; of_property_read_string(node, "qcom,proxy-consumer-name", &supply_name); consumer->reg = regulator_get(dev, supply_name); if (IS_ERR_OR_NULL(consumer->reg)) { ret = PTR_ERR(consumer->reg); pr_err("regulator_get(%s) failed for %s, ret=%d\n", supply_name, reg_name, ret); goto free_consumer; } if (consumer->max_uV > 0 && consumer->min_uV <= consumer->max_uV) { ret = regulator_set_voltage(consumer->reg, consumer->min_uV, consumer->max_uV); if (ret) { pr_err("regulator_set_voltage %s failed, ret=%d\n", reg_name, ret); goto free_regulator; } } if (consumer->current_uA > 0) { ret = regulator_set_load(consumer->reg, consumer->current_uA); if (ret < 0) { pr_err("regulator_set_load %s failed, ret=%d\n", reg_name, ret); goto remove_voltage; } } if (consumer->enable) { ret = regulator_enable(consumer->reg); if (ret) { pr_err("regulator_enable %s failed, ret=%d\n", reg_name, ret); goto remove_current; } } list_add(&consumer->list, &proxy_consumer_list); mutex_unlock(&proxy_consumer_list_lock); return consumer; remove_current: regulator_set_load(consumer->reg, 0); remove_voltage: regulator_set_voltage(consumer->reg, 0, INT_MAX); free_regulator: regulator_put(consumer->reg); free_consumer: kfree(consumer); unlock_list: mutex_unlock(&proxy_consumer_list_lock); return ERR_PTR(ret); } /** * regulator_proxy_consumer_register() - conditionally register a proxy consumer * for the specified regulator and set its boot time parameters * @dev: Device pointer of the regulator * @node: Device node pointer of the regulator * * This function calls regulator_get() after first checking if any proxy * consumer properties are present in the 'node' device node. After that, the * voltage, minimum current, and/or the enable state will be set based upon the * device node property values. * * Returns 0 on successfully proxy voting or if no proxy voting is needed, or an * errno if an error occurred. */ int regulator_proxy_consumer_register(struct device *dev, struct device_node *node) { struct proxy_consumer *consumer; consumer = regulator_proxy_consumer_add(dev, node); return PTR_ERR_OR_ZERO(consumer); } EXPORT_SYMBOL(regulator_proxy_consumer_register); /* proxy_consumer_list_lock must be held by caller. */ static int regulator_proxy_consumer_remove(struct proxy_consumer *consumer) { int ret = 0; if (consumer->enable) { ret = regulator_disable(consumer->reg); if (ret) pr_err("regulator_disable failed, ret=%d\n", ret); } if (consumer->current_uA > 0) { ret = regulator_set_load(consumer->reg, 0); if (ret < 0) pr_err("regulator_set_load failed, ret=%d\n", ret); } if (consumer->max_uV > 0 && consumer->min_uV <= consumer->max_uV) { ret = regulator_set_voltage(consumer->reg, 0, INT_MAX); if (ret) pr_err("regulator_set_voltage failed, ret=%d\n", ret); } regulator_put(consumer->reg); list_del(&consumer->list); kfree(consumer); return ret; } /** * regulator_proxy_consumer_unregister() - unregister the proxy consumers of a * device and remove their boot time * requests * @dev: Device pointer of the regulator * * This function removes all requests made by the proxy consumers of regulators * in dev which where issued in regulator_proxy_consumer_register() and then * frees the consumers' resources. * * Returns 0 on success or an errno on failure. */ void regulator_proxy_consumer_unregister(struct device *dev) { struct proxy_consumer *consumer, *temp; if (IS_ERR_OR_NULL(dev)) { pr_err("invalid device pointer\n"); return; } mutex_lock(&proxy_consumer_list_lock); list_for_each_entry_safe(consumer, temp, &proxy_consumer_list, list) { if (consumer->dev == dev) regulator_proxy_consumer_remove(consumer); } mutex_unlock(&proxy_consumer_list_lock); } EXPORT_SYMBOL(regulator_proxy_consumer_unregister); /* proxy_consumer_list_lock must be held by caller. */ static void _devm_regulator_proxy_consumer_release(struct device *dev, void *res) { struct proxy_consumer *consumer = *(struct proxy_consumer **)res; struct proxy_consumer *temp; bool found = false; /* * The proxy consumer may have already been removed due to a * sync_state() or devm_regulator_proxy_consumer_unregister() call. * Therefore, verify that it is still in the list before attempting to * remove it. */ list_for_each_entry(temp, &proxy_consumer_list, list) { if (temp == consumer) { found = true; break; } } if (found) regulator_proxy_consumer_remove(consumer); } static void devm_regulator_proxy_consumer_release(struct device *dev, void *res) { mutex_lock(&proxy_consumer_list_lock); _devm_regulator_proxy_consumer_release(dev, res); mutex_unlock(&proxy_consumer_list_lock); } /** * devm_regulator_proxy_consumer_register() - resource managed version of * regulator_proxy_consumer_register() * @dev: Device pointer of the regulator * @node: Device node pointer of the regulator * * This is a resource managed version of regulator_proxy_consumer_register(). * Proxy consumer requests made via this call are automatically removed via * regulator_proxy_consumer_unregister() on driver detach. See * regulator_proxy_consumer_register() for more details. * * Returns 0 on success or an errno on failure. */ int devm_regulator_proxy_consumer_register(struct device *dev, struct device_node *node) { struct proxy_consumer *consumer; struct proxy_consumer **ptr; ptr = devres_alloc(devm_regulator_proxy_consumer_release, sizeof(*ptr), GFP_KERNEL); if (!ptr) return -ENOMEM; consumer = regulator_proxy_consumer_add(dev, node); if (IS_ERR_OR_NULL(consumer)) { devres_free(ptr); return PTR_ERR(consumer); } *ptr = consumer; devres_add(dev, ptr); return 0; } EXPORT_SYMBOL(devm_regulator_proxy_consumer_register); static int devm_regulator_proxy_consumer_match(struct device *dev, void *res, void *data) { struct proxy_consumer **consumer = res; if (!consumer || !*consumer) { WARN_ON(!consumer || !*consumer); return 0; } return *consumer == data; } /** * devm_regulator_proxy_consumer_unregister() - resource managed version of * regulator_proxy_consumer_unregister() * @dev: Device pointer of the regulator * * Deallocate the proxy consumers allocated for 'dev' with * devm_regulator_proxy_consumer_register(). Normally this function will not * need to be called and the resource management code will ensure that the * resource is freed. * * Returns 0 on success or an errno on failure. */ void devm_regulator_proxy_consumer_unregister(struct device *dev) { struct proxy_consumer *consumer, *temp; if (IS_ERR_OR_NULL(dev)) return; mutex_lock(&proxy_consumer_list_lock); list_for_each_entry_safe(consumer, temp, &proxy_consumer_list, list) { if (consumer->dev == dev) devres_release(dev, _devm_regulator_proxy_consumer_release, devm_regulator_proxy_consumer_match, consumer); } mutex_unlock(&proxy_consumer_list_lock); } EXPORT_SYMBOL(devm_regulator_proxy_consumer_unregister); #ifndef CONFIG_REGULATOR_PROXY_CONSUMER_LEGACY void regulator_proxy_consumer_sync_state(struct device *dev) { regulator_proxy_consumer_unregister(dev); } EXPORT_SYMBOL(regulator_proxy_consumer_sync_state); #else /* CONFIG_REGULATOR_PROXY_CONSUMER_LEGACY=y */ void regulator_proxy_consumer_sync_state(struct device *dev) { } EXPORT_SYMBOL(regulator_proxy_consumer_sync_state); /* * Remove all proxy requests at late_initcall_sync. The assumption is that all * devices have probed at this point and made their own regulator requests. */ static int __init regulator_proxy_consumer_remove_all(void) { struct proxy_consumer *consumer; struct proxy_consumer *temp; mutex_lock(&proxy_consumer_list_lock); proxy_consumers_removed = true; if (!list_empty(&proxy_consumer_list)) pr_info("removing regulator proxy consumer requests\n"); list_for_each_entry_safe(consumer, temp, &proxy_consumer_list, list) { regulator_proxy_consumer_remove(consumer); } mutex_unlock(&proxy_consumer_list_lock); return 0; } late_initcall_sync(regulator_proxy_consumer_remove_all); #endif MODULE_DESCRIPTION("Regulator proxy consumer library"); MODULE_LICENSE("GPL v2");