Commit 3801b86a authored by Mark Brown's avatar Mark Brown Committed by Liam Girdwood

regulator: Refactor supply implementation to work as regular consumers

Currently the regulator supply implementation is somewhat complex and
fragile as it doesn't look like standard consumers but is instead a
parallel implementation. This causes issues with locking and reference
counting.

Move the implementation over to using standard consumers to address this.
Rather than only notifying the supply on the first enable/disable we do so
every time the regulator is enabled or disabled, simplifying locking as we
don't need to hold a lock on the consumer we are about to enable.
Signed-off-by: default avatarMark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: default avatarLiam Girdwood <lrg@slimlogic.co.uk>
parent e0eaedef
...@@ -82,8 +82,7 @@ struct regulator { ...@@ -82,8 +82,7 @@ struct regulator {
}; };
static int _regulator_is_enabled(struct regulator_dev *rdev); static int _regulator_is_enabled(struct regulator_dev *rdev);
static int _regulator_disable(struct regulator_dev *rdev, static int _regulator_disable(struct regulator_dev *rdev);
struct regulator_dev **supply_rdev_ptr);
static int _regulator_get_voltage(struct regulator_dev *rdev); static int _regulator_get_voltage(struct regulator_dev *rdev);
static int _regulator_get_current_limit(struct regulator_dev *rdev); static int _regulator_get_current_limit(struct regulator_dev *rdev);
static unsigned int _regulator_get_mode(struct regulator_dev *rdev); static unsigned int _regulator_get_mode(struct regulator_dev *rdev);
...@@ -91,6 +90,9 @@ static void _notifier_call_chain(struct regulator_dev *rdev, ...@@ -91,6 +90,9 @@ static void _notifier_call_chain(struct regulator_dev *rdev,
unsigned long event, void *data); unsigned long event, void *data);
static int _regulator_do_set_voltage(struct regulator_dev *rdev, static int _regulator_do_set_voltage(struct regulator_dev *rdev,
int min_uV, int max_uV); int min_uV, int max_uV);
static struct regulator *create_regulator(struct regulator_dev *rdev,
struct device *dev,
const char *supply_name);
static const char *rdev_get_name(struct regulator_dev *rdev) static const char *rdev_get_name(struct regulator_dev *rdev)
{ {
...@@ -930,21 +932,20 @@ static int set_machine_constraints(struct regulator_dev *rdev, ...@@ -930,21 +932,20 @@ static int set_machine_constraints(struct regulator_dev *rdev,
* core if it's child is enabled. * core if it's child is enabled.
*/ */
static int set_supply(struct regulator_dev *rdev, static int set_supply(struct regulator_dev *rdev,
struct regulator_dev *supply_rdev) struct regulator_dev *supply_rdev)
{ {
int err; int err;
err = sysfs_create_link(&rdev->dev.kobj, &supply_rdev->dev.kobj, rdev_info(rdev, "supplied by %s\n", rdev_get_name(supply_rdev));
"supply");
if (err) { rdev->supply = create_regulator(supply_rdev, &rdev->dev, "SUPPLY");
rdev_err(rdev, "could not add device link %s err %d\n", if (IS_ERR(rdev->supply)) {
supply_rdev->dev.kobj.name, err); err = PTR_ERR(rdev->supply);
goto out; rdev->supply = NULL;
return err;
} }
rdev->supply = supply_rdev;
list_add(&rdev->slist, &supply_rdev->supply_list); return 0;
out:
return err;
} }
/** /**
...@@ -1303,19 +1304,6 @@ static int _regulator_enable(struct regulator_dev *rdev) ...@@ -1303,19 +1304,6 @@ static int _regulator_enable(struct regulator_dev *rdev)
{ {
int ret, delay; int ret, delay;
if (rdev->use_count == 0) {
/* do we need to enable the supply regulator first */
if (rdev->supply) {
mutex_lock(&rdev->supply->mutex);
ret = _regulator_enable(rdev->supply);
mutex_unlock(&rdev->supply->mutex);
if (ret < 0) {
rdev_err(rdev, "failed to enable: %d\n", ret);
return ret;
}
}
}
/* check voltage and requested load before enabling */ /* check voltage and requested load before enabling */
if (rdev->constraints && if (rdev->constraints &&
(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_DRMS)) (rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_DRMS))
...@@ -1390,19 +1378,27 @@ int regulator_enable(struct regulator *regulator) ...@@ -1390,19 +1378,27 @@ int regulator_enable(struct regulator *regulator)
struct regulator_dev *rdev = regulator->rdev; struct regulator_dev *rdev = regulator->rdev;
int ret = 0; int ret = 0;
if (rdev->supply) {
ret = regulator_enable(rdev->supply);
if (ret != 0)
return ret;
}
mutex_lock(&rdev->mutex); mutex_lock(&rdev->mutex);
ret = _regulator_enable(rdev); ret = _regulator_enable(rdev);
mutex_unlock(&rdev->mutex); mutex_unlock(&rdev->mutex);
if (ret != 0)
regulator_disable(rdev->supply);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(regulator_enable); EXPORT_SYMBOL_GPL(regulator_enable);
/* locks held by regulator_disable() */ /* locks held by regulator_disable() */
static int _regulator_disable(struct regulator_dev *rdev, static int _regulator_disable(struct regulator_dev *rdev)
struct regulator_dev **supply_rdev_ptr)
{ {
int ret = 0; int ret = 0;
*supply_rdev_ptr = NULL;
if (WARN(rdev->use_count <= 0, if (WARN(rdev->use_count <= 0,
"unbalanced disables for %s\n", rdev_get_name(rdev))) "unbalanced disables for %s\n", rdev_get_name(rdev)))
...@@ -1429,9 +1425,6 @@ static int _regulator_disable(struct regulator_dev *rdev, ...@@ -1429,9 +1425,6 @@ static int _regulator_disable(struct regulator_dev *rdev,
NULL); NULL);
} }
/* decrease our supplies ref count and disable if required */
*supply_rdev_ptr = rdev->supply;
rdev->use_count = 0; rdev->use_count = 0;
} else if (rdev->use_count > 1) { } else if (rdev->use_count > 1) {
...@@ -1442,6 +1435,7 @@ static int _regulator_disable(struct regulator_dev *rdev, ...@@ -1442,6 +1435,7 @@ static int _regulator_disable(struct regulator_dev *rdev,
rdev->use_count--; rdev->use_count--;
} }
return ret; return ret;
} }
...@@ -1460,29 +1454,21 @@ static int _regulator_disable(struct regulator_dev *rdev, ...@@ -1460,29 +1454,21 @@ static int _regulator_disable(struct regulator_dev *rdev,
int regulator_disable(struct regulator *regulator) int regulator_disable(struct regulator *regulator)
{ {
struct regulator_dev *rdev = regulator->rdev; struct regulator_dev *rdev = regulator->rdev;
struct regulator_dev *supply_rdev = NULL;
int ret = 0; int ret = 0;
mutex_lock(&rdev->mutex); mutex_lock(&rdev->mutex);
ret = _regulator_disable(rdev, &supply_rdev); ret = _regulator_disable(rdev);
mutex_unlock(&rdev->mutex); mutex_unlock(&rdev->mutex);
/* decrease our supplies ref count and disable if required */ if (ret == 0 && rdev->supply)
while (supply_rdev != NULL) { regulator_disable(rdev->supply);
rdev = supply_rdev;
mutex_lock(&rdev->mutex);
_regulator_disable(rdev, &supply_rdev);
mutex_unlock(&rdev->mutex);
}
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(regulator_disable); EXPORT_SYMBOL_GPL(regulator_disable);
/* locks held by regulator_force_disable() */ /* locks held by regulator_force_disable() */
static int _regulator_force_disable(struct regulator_dev *rdev, static int _regulator_force_disable(struct regulator_dev *rdev)
struct regulator_dev **supply_rdev_ptr)
{ {
int ret = 0; int ret = 0;
...@@ -1499,10 +1485,6 @@ static int _regulator_force_disable(struct regulator_dev *rdev, ...@@ -1499,10 +1485,6 @@ static int _regulator_force_disable(struct regulator_dev *rdev,
REGULATOR_EVENT_DISABLE, NULL); REGULATOR_EVENT_DISABLE, NULL);
} }
/* decrease our supplies ref count and disable if required */
*supply_rdev_ptr = rdev->supply;
rdev->use_count = 0;
return ret; return ret;
} }
...@@ -1518,16 +1500,16 @@ static int _regulator_force_disable(struct regulator_dev *rdev, ...@@ -1518,16 +1500,16 @@ static int _regulator_force_disable(struct regulator_dev *rdev,
int regulator_force_disable(struct regulator *regulator) int regulator_force_disable(struct regulator *regulator)
{ {
struct regulator_dev *rdev = regulator->rdev; struct regulator_dev *rdev = regulator->rdev;
struct regulator_dev *supply_rdev = NULL;
int ret; int ret;
mutex_lock(&rdev->mutex); mutex_lock(&rdev->mutex);
regulator->uA_load = 0; regulator->uA_load = 0;
ret = _regulator_force_disable(rdev, &supply_rdev); ret = _regulator_force_disable(regulator->rdev);
mutex_unlock(&rdev->mutex); mutex_unlock(&rdev->mutex);
if (supply_rdev) if (rdev->supply)
regulator_disable(get_device_regulator(rdev_get_dev(supply_rdev))); while (rdev->open_count--)
regulator_disable(rdev->supply);
return ret; return ret;
} }
...@@ -2138,7 +2120,7 @@ int regulator_set_optimum_mode(struct regulator *regulator, int uA_load) ...@@ -2138,7 +2120,7 @@ int regulator_set_optimum_mode(struct regulator *regulator, int uA_load)
/* get input voltage */ /* get input voltage */
input_uV = 0; input_uV = 0;
if (rdev->supply) if (rdev->supply)
input_uV = _regulator_get_voltage(rdev->supply); input_uV = regulator_get_voltage(rdev->supply);
if (input_uV <= 0) if (input_uV <= 0)
input_uV = rdev->constraints->input_uV; input_uV = rdev->constraints->input_uV;
if (input_uV <= 0) { if (input_uV <= 0) {
...@@ -2208,17 +2190,8 @@ EXPORT_SYMBOL_GPL(regulator_unregister_notifier); ...@@ -2208,17 +2190,8 @@ EXPORT_SYMBOL_GPL(regulator_unregister_notifier);
static void _notifier_call_chain(struct regulator_dev *rdev, static void _notifier_call_chain(struct regulator_dev *rdev,
unsigned long event, void *data) unsigned long event, void *data)
{ {
struct regulator_dev *_rdev;
/* call rdev chain first */ /* call rdev chain first */
blocking_notifier_call_chain(&rdev->notifier, event, NULL); blocking_notifier_call_chain(&rdev->notifier, event, NULL);
/* now notify regulator we supply */
list_for_each_entry(_rdev, &rdev->supply_list, slist) {
mutex_lock(&_rdev->mutex);
_notifier_call_chain(_rdev, event, data);
mutex_unlock(&_rdev->mutex);
}
} }
/** /**
...@@ -2610,9 +2583,7 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, ...@@ -2610,9 +2583,7 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc,
rdev->owner = regulator_desc->owner; rdev->owner = regulator_desc->owner;
rdev->desc = regulator_desc; rdev->desc = regulator_desc;
INIT_LIST_HEAD(&rdev->consumer_list); INIT_LIST_HEAD(&rdev->consumer_list);
INIT_LIST_HEAD(&rdev->supply_list);
INIT_LIST_HEAD(&rdev->list); INIT_LIST_HEAD(&rdev->list);
INIT_LIST_HEAD(&rdev->slist);
BLOCKING_INIT_NOTIFIER_HEAD(&rdev->notifier); BLOCKING_INIT_NOTIFIER_HEAD(&rdev->notifier);
/* preform any regulator specific init */ /* preform any regulator specific init */
...@@ -2724,7 +2695,7 @@ void regulator_unregister(struct regulator_dev *rdev) ...@@ -2724,7 +2695,7 @@ void regulator_unregister(struct regulator_dev *rdev)
unset_regulator_supplies(rdev); unset_regulator_supplies(rdev);
list_del(&rdev->list); list_del(&rdev->list);
if (rdev->supply) if (rdev->supply)
sysfs_remove_link(&rdev->dev.kobj, "supply"); regulator_put(rdev->supply);
device_unregister(&rdev->dev); device_unregister(&rdev->dev);
kfree(rdev->constraints); kfree(rdev->constraints);
mutex_unlock(&regulator_list_mutex); mutex_unlock(&regulator_list_mutex);
......
...@@ -188,18 +188,16 @@ struct regulator_dev { ...@@ -188,18 +188,16 @@ struct regulator_dev {
/* lists we belong to */ /* lists we belong to */
struct list_head list; /* list of all regulators */ struct list_head list; /* list of all regulators */
struct list_head slist; /* list of supplied regulators */
/* lists we own */ /* lists we own */
struct list_head consumer_list; /* consumers we supply */ struct list_head consumer_list; /* consumers we supply */
struct list_head supply_list; /* regulators we supply */
struct blocking_notifier_head notifier; struct blocking_notifier_head notifier;
struct mutex mutex; /* consumer lock */ struct mutex mutex; /* consumer lock */
struct module *owner; struct module *owner;
struct device dev; struct device dev;
struct regulation_constraints *constraints; struct regulation_constraints *constraints;
struct regulator_dev *supply; /* for tree */ struct regulator *supply; /* for tree */
void *reg_data; /* regulator_dev data */ void *reg_data; /* regulator_dev data */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment