aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/regulator/core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/regulator/core.c')
-rw-r--r--drivers/regulator/core.c115
1 files changed, 90 insertions, 25 deletions
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 03154f5b939f..75ff7c563c5d 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -105,6 +105,7 @@ static int regulator_balance_voltage(struct regulator_dev *rdev,
static struct regulator *create_regulator(struct regulator_dev *rdev,
struct device *dev,
const char *supply_name);
+static void destroy_regulator(struct regulator *regulator);
static void _regulator_put(struct regulator *regulator);
const char *rdev_get_name(struct regulator_dev *rdev)
@@ -2034,20 +2035,9 @@ struct regulator *regulator_get_optional(struct device *dev, const char *id)
}
EXPORT_SYMBOL_GPL(regulator_get_optional);
-/* regulator_list_mutex lock held by regulator_put() */
-static void _regulator_put(struct regulator *regulator)
+static void destroy_regulator(struct regulator *regulator)
{
- struct regulator_dev *rdev;
-
- if (IS_ERR_OR_NULL(regulator))
- return;
-
- lockdep_assert_held_once(&regulator_list_mutex);
-
- /* Docs say you must disable before calling regulator_put() */
- WARN_ON(regulator->enable_count);
-
- rdev = regulator->rdev;
+ struct regulator_dev *rdev = regulator->rdev;
debugfs_remove_recursive(regulator->debugfs);
@@ -2068,6 +2058,24 @@ static void _regulator_put(struct regulator *regulator)
kfree_const(regulator->supply_name);
kfree(regulator);
+}
+
+/* regulator_list_mutex lock held by regulator_put() */
+static void _regulator_put(struct regulator *regulator)
+{
+ struct regulator_dev *rdev;
+
+ if (IS_ERR_OR_NULL(regulator))
+ return;
+
+ lockdep_assert_held_once(&regulator_list_mutex);
+
+ /* Docs say you must disable before calling regulator_put() */
+ WARN_ON(regulator->enable_count);
+
+ rdev = regulator->rdev;
+
+ destroy_regulator(regulator);
module_put(rdev->owner);
put_device(&rdev->dev);
@@ -2347,6 +2355,37 @@ static void _regulator_enable_delay(unsigned int delay)
udelay(us);
}
+/**
+ * _regulator_check_status_enabled
+ *
+ * A helper function to check if the regulator status can be interpreted
+ * as 'regulator is enabled'.
+ * @rdev: the regulator device to check
+ *
+ * Return:
+ * * 1 - if status shows regulator is in enabled state
+ * * 0 - if not enabled state
+ * * Error Value - as received from ops->get_status()
+ */
+static inline int _regulator_check_status_enabled(struct regulator_dev *rdev)
+{
+ int ret = rdev->desc->ops->get_status(rdev);
+
+ if (ret < 0) {
+ rdev_info(rdev, "get_status returned error: %d\n", ret);
+ return ret;
+ }
+
+ switch (ret) {
+ case REGULATOR_STATUS_OFF:
+ case REGULATOR_STATUS_ERROR:
+ case REGULATOR_STATUS_UNDEFINED:
+ return 0;
+ default:
+ return 1;
+ }
+}
+
static int _regulator_do_enable(struct regulator_dev *rdev)
{
int ret, delay;
@@ -2407,7 +2446,37 @@ static int _regulator_do_enable(struct regulator_dev *rdev)
* together. */
trace_regulator_enable_delay(rdev_get_name(rdev));
- _regulator_enable_delay(delay);
+ /* If poll_enabled_time is set, poll upto the delay calculated
+ * above, delaying poll_enabled_time uS to check if the regulator
+ * actually got enabled.
+ * If the regulator isn't enabled after enable_delay has
+ * expired, return -ETIMEDOUT.
+ */
+ if (rdev->desc->poll_enabled_time) {
+ unsigned int time_remaining = delay;
+
+ while (time_remaining > 0) {
+ _regulator_enable_delay(rdev->desc->poll_enabled_time);
+
+ if (rdev->desc->ops->get_status) {
+ ret = _regulator_check_status_enabled(rdev);
+ if (ret < 0)
+ return ret;
+ else if (ret)
+ break;
+ } else if (rdev->desc->ops->is_enabled(rdev))
+ break;
+
+ time_remaining -= rdev->desc->poll_enabled_time;
+ }
+
+ if (time_remaining <= 0) {
+ rdev_err(rdev, "Enabled check timed out\n");
+ return -ETIMEDOUT;
+ }
+ } else {
+ _regulator_enable_delay(delay);
+ }
trace_regulator_enable_complete(rdev_get_name(rdev));
@@ -5023,7 +5092,6 @@ regulator_register(const struct regulator_desc *regulator_desc,
struct regulator_dev *rdev;
bool dangling_cfg_gpiod = false;
bool dangling_of_gpiod = false;
- bool reg_device_fail = false;
struct device *dev;
int ret, i;
@@ -5152,10 +5220,12 @@ regulator_register(const struct regulator_desc *regulator_desc,
}
/* register with sysfs */
+ device_initialize(&rdev->dev);
rdev->dev.class = &regulator_class;
rdev->dev.parent = dev;
dev_set_name(&rdev->dev, "regulator.%lu",
(unsigned long) atomic_inc_return(&regulator_no));
+ dev_set_drvdata(&rdev->dev, rdev);
/* set regulator constraints */
if (init_data)
@@ -5206,12 +5276,9 @@ regulator_register(const struct regulator_desc *regulator_desc,
!rdev->desc->fixed_uV)
rdev->is_switch = true;
- dev_set_drvdata(&rdev->dev, rdev);
- ret = device_register(&rdev->dev);
- if (ret != 0) {
- reg_device_fail = true;
+ ret = device_add(&rdev->dev);
+ if (ret != 0)
goto unset_supplies;
- }
rdev_init_debugfs(rdev);
@@ -5233,17 +5300,15 @@ unset_supplies:
mutex_unlock(&regulator_list_mutex);
wash:
kfree(rdev->coupling_desc.coupled_rdevs);
- kfree(rdev->constraints);
mutex_lock(&regulator_list_mutex);
regulator_ena_gpio_free(rdev);
mutex_unlock(&regulator_list_mutex);
+ put_device(&rdev->dev);
+ rdev = NULL;
clean:
if (dangling_of_gpiod)
gpiod_put(config->ena_gpiod);
- if (reg_device_fail)
- put_device(&rdev->dev);
- else
- kfree(rdev);
+ kfree(rdev);
kfree(config);
rinse:
if (dangling_cfg_gpiod)