ANDROID: GKI: regulator: Add proxy consumer driver

Add a proxy consumer driver which can be used to ensure that a
given regulator maintains a certain minimum state during bootup.
Enable state, voltage, and current may be forced to specified
levels.

Bug: 150508586
Change-Id: I0ccc63a41684462684ac737fb2f4129a3e6e4aea
Signed-off-by: David Collins <collinsd@codeaurora.org>
(cherry picked from commit cf03466c07)
[Also squashed e9a05bb4d3 "spdx: Modify spdx tag from GPL-2.0 to GPL-2.0-only"]
Signed-off-by: Saravana Kannan <saravanak@google.com>
This commit is contained in:
David Collins 2013-06-10 15:38:04 -07:00 committed by Saravana Kannan
parent b4270a93c7
commit 653a867e09
5 changed files with 298 additions and 0 deletions

View file

@ -0,0 +1,32 @@
Regulator Proxy Consumer Bindings
Regulator proxy consumers provide a means to use a default regulator state
during bootup only which is removed at the end of boot. This feature can be
used in situations where a shared regulator can be scaled between several
possible voltages and hardware requires that it be at a high level at the
beginning of boot before the consumer device responsible for requesting the
high level has probed.
Optional properties:
proxy-supply: phandle of the regulator's own device node.
This property is required if any of the three
properties below are specified.
qcom,proxy-consumer-enable: Boolean indicating that the regulator must be
kept enabled during boot.
qcom,proxy-consumer-voltage: List of two integers corresponding the minimum
and maximum voltage allowed during boot in
microvolts.
qcom,proxy-consumer-current: Minimum current in microamps required during
boot.
Example:
foo_vreg: regulator@0 {
regulator-name = "foo";
regulator-min-microvolt = <1000000>;
regulator-max-microvolt = <2000000>;
proxy-supply = <&foo_vreg>;
qcom,proxy-consumer-voltage = <1500000 2000000>;
qcom,proxy-consumer-current = <25000>;
qcom,proxy-consumer-enable;
};

View file

@ -54,6 +54,16 @@ config REGULATOR_USERSPACE_CONSUMER
If unsure, say no.
config REGULATOR_PROXY_CONSUMER
bool "Boot time regulator proxy consumer support"
help
This driver 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_88PG86X
tristate "Marvell 88PG86X voltage regulators"
depends on I2C

View file

@ -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

View file

@ -0,0 +1,222 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2013-2014, 2016, 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/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;
bool enable;
int min_uV;
int max_uV;
u32 current_uA;
};
static DEFINE_MUTEX(proxy_consumer_list_mutex);
static LIST_HEAD(proxy_consumer_list);
static bool proxy_consumers_removed;
/**
* regulator_proxy_consumer_register() - conditionally register a proxy consumer
* for the specified regulator and set its boot time parameters
* @reg_dev: Device pointer of the regulator
* @reg_node: Device node pointer of the regulator
*
* Returns a struct proxy_consumer pointer corresponding to the regulator on
* success, ERR_PTR() if an error occurred, or NULL if no proxy consumer is
* needed for the regulator. This function calls
* regulator_get(reg_dev, "proxy") after first checking if any proxy consumer
* properties are present in the reg_node device node. After that, the voltage,
* minimum current, and/or the enable state will be set based upon the device
* node property values.
*/
struct proxy_consumer *regulator_proxy_consumer_register(struct device *reg_dev,
struct device_node *reg_node)
{
struct proxy_consumer *consumer = NULL;
const char *reg_name = "";
u32 voltage[2] = {0};
int rc;
/* Return immediately if no proxy consumer properties are specified. */
if (!of_find_property(reg_node, "qcom,proxy-consumer-enable", NULL)
&& !of_find_property(reg_node, "qcom,proxy-consumer-voltage", NULL)
&& !of_find_property(reg_node, "qcom,proxy-consumer-current", NULL))
return NULL;
mutex_lock(&proxy_consumer_list_mutex);
/* Do not register new consumers if they cannot be removed later. */
if (proxy_consumers_removed) {
rc = -EPERM;
goto unlock;
}
if (dev_name(reg_dev))
reg_name = dev_name(reg_dev);
consumer = kzalloc(sizeof(*consumer), GFP_KERNEL);
if (!consumer) {
rc = -ENOMEM;
goto unlock;
}
consumer->enable
= of_property_read_bool(reg_node, "qcom,proxy-consumer-enable");
of_property_read_u32(reg_node, "qcom,proxy-consumer-current",
&consumer->current_uA);
rc = of_property_read_u32_array(reg_node, "qcom,proxy-consumer-voltage",
voltage, 2);
if (!rc) {
consumer->min_uV = voltage[0];
consumer->max_uV = voltage[1];
}
dev_dbg(reg_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);
consumer->reg = regulator_get(reg_dev, "proxy");
if (IS_ERR_OR_NULL(consumer->reg)) {
rc = PTR_ERR(consumer->reg);
pr_err("regulator_get() failed for %s, rc=%d\n", reg_name, rc);
goto unlock;
}
if (consumer->max_uV > 0 && consumer->min_uV <= consumer->max_uV) {
rc = regulator_set_voltage(consumer->reg, consumer->min_uV,
consumer->max_uV);
if (rc) {
pr_err("regulator_set_voltage %s failed, rc=%d\n",
reg_name, rc);
goto free_regulator;
}
}
if (consumer->current_uA > 0) {
rc = regulator_set_load(consumer->reg,
consumer->current_uA);
if (rc < 0) {
pr_err("regulator_set_load %s failed, rc=%d\n",
reg_name, rc);
goto remove_voltage;
}
}
if (consumer->enable) {
rc = regulator_enable(consumer->reg);
if (rc) {
pr_err("regulator_enable %s failed, rc=%d\n", reg_name,
rc);
goto remove_current;
}
}
list_add(&consumer->list, &proxy_consumer_list);
mutex_unlock(&proxy_consumer_list_mutex);
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);
unlock:
kfree(consumer);
mutex_unlock(&proxy_consumer_list_mutex);
return ERR_PTR(rc);
}
/* proxy_consumer_list_mutex must be held by caller. */
static int regulator_proxy_consumer_remove(struct proxy_consumer *consumer)
{
int rc = 0;
if (consumer->enable) {
rc = regulator_disable(consumer->reg);
if (rc)
pr_err("regulator_disable failed, rc=%d\n", rc);
}
if (consumer->current_uA > 0) {
rc = regulator_set_load(consumer->reg, 0);
if (rc < 0)
pr_err("regulator_set_load failed, rc=%d\n",
rc);
}
if (consumer->max_uV > 0 && consumer->min_uV <= consumer->max_uV) {
rc = regulator_set_voltage(consumer->reg, 0, INT_MAX);
if (rc)
pr_err("regulator_set_voltage failed, rc=%d\n", rc);
}
regulator_put(consumer->reg);
list_del(&consumer->list);
kfree(consumer);
return rc;
}
/**
* regulator_proxy_consumer_unregister() - unregister a proxy consumer and
* remove its boot time requests
* @consumer: Pointer to proxy_consumer to be removed
*
* Returns 0 on success or errno on failure. This function removes all requests
* made by the proxy consumer in regulator_proxy_consumer_register() and then
* frees the consumer's resources.
*/
int regulator_proxy_consumer_unregister(struct proxy_consumer *consumer)
{
int rc = 0;
if (IS_ERR_OR_NULL(consumer))
return 0;
mutex_lock(&proxy_consumer_list_mutex);
if (!proxy_consumers_removed)
rc = regulator_proxy_consumer_remove(consumer);
mutex_unlock(&proxy_consumer_list_mutex);
return rc;
}
/*
* 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_mutex);
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_mutex);
return 0;
}
late_initcall_sync(regulator_proxy_consumer_remove_all);

View file

@ -0,0 +1,33 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2013, The Linux Foundation. All rights reserved.
*/
#ifndef _LINUX_REGULATOR_PROXY_CONSUMER_H_
#define _LINUX_REGULATOR_PROXY_CONSUMER_H_
#include <linux/device.h>
#include <linux/of.h>
struct proxy_consumer;
#ifdef CONFIG_REGULATOR_PROXY_CONSUMER
struct proxy_consumer *regulator_proxy_consumer_register(struct device *reg_dev,
struct device_node *reg_node);
int regulator_proxy_consumer_unregister(struct proxy_consumer *consumer);
#else
static inline struct proxy_consumer *regulator_proxy_consumer_register(
struct device *reg_dev, struct device_node *reg_node)
{ return NULL; }
static inline int regulator_proxy_consumer_unregister(
struct proxy_consumer *consumer)
{ return 0; }
#endif
#endif