|
@@ -19,6 +19,7 @@
|
|
#include <linux/device.h>
|
|
#include <linux/device.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of.h>
|
|
#include <linux/export.h>
|
|
#include <linux/export.h>
|
|
|
|
+#include <linux/regulator/consumer.h>
|
|
|
|
|
|
#include "opp.h"
|
|
#include "opp.h"
|
|
|
|
|
|
@@ -565,6 +566,9 @@ static void _remove_device_opp(struct device_opp *dev_opp)
|
|
if (dev_opp->prop_name)
|
|
if (dev_opp->prop_name)
|
|
return;
|
|
return;
|
|
|
|
|
|
|
|
+ if (!IS_ERR_OR_NULL(dev_opp->regulator))
|
|
|
|
+ return;
|
|
|
|
+
|
|
list_dev = list_first_entry(&dev_opp->dev_list, struct device_list_opp,
|
|
list_dev = list_first_entry(&dev_opp->dev_list, struct device_list_opp,
|
|
node);
|
|
node);
|
|
|
|
|
|
@@ -1085,6 +1089,113 @@ unlock:
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(dev_pm_opp_put_prop_name);
|
|
EXPORT_SYMBOL_GPL(dev_pm_opp_put_prop_name);
|
|
|
|
|
|
|
|
+/**
|
|
|
|
+ * dev_pm_opp_set_regulator() - Set regulator name for the device
|
|
|
|
+ * @dev: Device for which regulator name is being set.
|
|
|
|
+ * @name: Name of the regulator.
|
|
|
|
+ *
|
|
|
|
+ * In order to support OPP switching, OPP layer needs to know the name of the
|
|
|
|
+ * device's regulator, as the core would be required to switch voltages as well.
|
|
|
|
+ *
|
|
|
|
+ * This must be called before any OPPs are initialized for the device.
|
|
|
|
+ *
|
|
|
|
+ * Locking: The internal device_opp and opp structures are RCU protected.
|
|
|
|
+ * Hence this function internally uses RCU updater strategy with mutex locks
|
|
|
|
+ * to keep the integrity of the internal data structures. Callers should ensure
|
|
|
|
+ * that this function is *NOT* called under RCU protection or in contexts where
|
|
|
|
+ * mutex cannot be locked.
|
|
|
|
+ */
|
|
|
|
+int dev_pm_opp_set_regulator(struct device *dev, const char *name)
|
|
|
|
+{
|
|
|
|
+ struct device_opp *dev_opp;
|
|
|
|
+ struct regulator *reg;
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ mutex_lock(&dev_opp_list_lock);
|
|
|
|
+
|
|
|
|
+ dev_opp = _add_device_opp(dev);
|
|
|
|
+ if (!dev_opp) {
|
|
|
|
+ ret = -ENOMEM;
|
|
|
|
+ goto unlock;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* This should be called before OPPs are initialized */
|
|
|
|
+ if (WARN_ON(!list_empty(&dev_opp->opp_list))) {
|
|
|
|
+ ret = -EBUSY;
|
|
|
|
+ goto err;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Already have a regulator set */
|
|
|
|
+ if (WARN_ON(!IS_ERR_OR_NULL(dev_opp->regulator))) {
|
|
|
|
+ ret = -EBUSY;
|
|
|
|
+ goto err;
|
|
|
|
+ }
|
|
|
|
+ /* Allocate the regulator */
|
|
|
|
+ reg = regulator_get_optional(dev, name);
|
|
|
|
+ if (IS_ERR(reg)) {
|
|
|
|
+ ret = PTR_ERR(reg);
|
|
|
|
+ if (ret != -EPROBE_DEFER)
|
|
|
|
+ dev_err(dev, "%s: no regulator (%s) found: %d\n",
|
|
|
|
+ __func__, name, ret);
|
|
|
|
+ goto err;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ dev_opp->regulator = reg;
|
|
|
|
+
|
|
|
|
+ mutex_unlock(&dev_opp_list_lock);
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+err:
|
|
|
|
+ _remove_device_opp(dev_opp);
|
|
|
|
+unlock:
|
|
|
|
+ mutex_unlock(&dev_opp_list_lock);
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL_GPL(dev_pm_opp_set_regulator);
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * dev_pm_opp_put_regulator() - Releases resources blocked for regulator
|
|
|
|
+ * @dev: Device for which regulator was set.
|
|
|
|
+ *
|
|
|
|
+ * Locking: The internal device_opp and opp structures are RCU protected.
|
|
|
|
+ * Hence this function internally uses RCU updater strategy with mutex locks
|
|
|
|
+ * to keep the integrity of the internal data structures. Callers should ensure
|
|
|
|
+ * that this function is *NOT* called under RCU protection or in contexts where
|
|
|
|
+ * mutex cannot be locked.
|
|
|
|
+ */
|
|
|
|
+void dev_pm_opp_put_regulator(struct device *dev)
|
|
|
|
+{
|
|
|
|
+ struct device_opp *dev_opp;
|
|
|
|
+
|
|
|
|
+ mutex_lock(&dev_opp_list_lock);
|
|
|
|
+
|
|
|
|
+ /* Check for existing list for 'dev' first */
|
|
|
|
+ dev_opp = _find_device_opp(dev);
|
|
|
|
+ if (IS_ERR(dev_opp)) {
|
|
|
|
+ dev_err(dev, "Failed to find dev_opp: %ld\n", PTR_ERR(dev_opp));
|
|
|
|
+ goto unlock;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (IS_ERR_OR_NULL(dev_opp->regulator)) {
|
|
|
|
+ dev_err(dev, "%s: Doesn't have regulator set\n", __func__);
|
|
|
|
+ goto unlock;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Make sure there are no concurrent readers while updating dev_opp */
|
|
|
|
+ WARN_ON(!list_empty(&dev_opp->opp_list));
|
|
|
|
+
|
|
|
|
+ regulator_put(dev_opp->regulator);
|
|
|
|
+ dev_opp->regulator = ERR_PTR(-EINVAL);
|
|
|
|
+
|
|
|
|
+ /* Try freeing device_opp if this was the last blocking resource */
|
|
|
|
+ _remove_device_opp(dev_opp);
|
|
|
|
+
|
|
|
|
+unlock:
|
|
|
|
+ mutex_unlock(&dev_opp_list_lock);
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL_GPL(dev_pm_opp_put_regulator);
|
|
|
|
+
|
|
static bool _opp_is_supported(struct device *dev, struct device_opp *dev_opp,
|
|
static bool _opp_is_supported(struct device *dev, struct device_opp *dev_opp,
|
|
struct device_node *np)
|
|
struct device_node *np)
|
|
{
|
|
{
|