Skip to content

Commit

Permalink
regulator: Refactor supply implementation to work as regular consumers
Browse files Browse the repository at this point in the history
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: Mark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: Liam Girdwood <lrg@slimlogic.co.uk>
  • Loading branch information
Mark Brown authored and Liam Girdwood committed Jun 9, 2011
1 parent e0eaede commit 3801b86
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 68 deletions.
101 changes: 36 additions & 65 deletions drivers/regulator/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,17 @@ struct regulator {
};

static int _regulator_is_enabled(struct regulator_dev *rdev);
static int _regulator_disable(struct regulator_dev *rdev,
struct regulator_dev **supply_rdev_ptr);
static int _regulator_disable(struct regulator_dev *rdev);
static int _regulator_get_voltage(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 void _notifier_call_chain(struct regulator_dev *rdev,
unsigned long event, void *data);
static int _regulator_do_set_voltage(struct regulator_dev *rdev,
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)
{
Expand Down Expand Up @@ -930,21 +932,20 @@ static int set_machine_constraints(struct regulator_dev *rdev,
* core if it's child is enabled.
*/
static int set_supply(struct regulator_dev *rdev,
struct regulator_dev *supply_rdev)
struct regulator_dev *supply_rdev)
{
int err;

err = sysfs_create_link(&rdev->dev.kobj, &supply_rdev->dev.kobj,
"supply");
if (err) {
rdev_err(rdev, "could not add device link %s err %d\n",
supply_rdev->dev.kobj.name, err);
goto out;
rdev_info(rdev, "supplied by %s\n", rdev_get_name(supply_rdev));

rdev->supply = create_regulator(supply_rdev, &rdev->dev, "SUPPLY");
if (IS_ERR(rdev->supply)) {
err = PTR_ERR(rdev->supply);
rdev->supply = NULL;
return err;
}
rdev->supply = supply_rdev;
list_add(&rdev->slist, &supply_rdev->supply_list);
out:
return err;

return 0;
}

/**
Expand Down Expand Up @@ -1303,19 +1304,6 @@ static int _regulator_enable(struct regulator_dev *rdev)
{
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 */
if (rdev->constraints &&
(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_DRMS))
Expand Down Expand Up @@ -1390,19 +1378,27 @@ int regulator_enable(struct regulator *regulator)
struct regulator_dev *rdev = regulator->rdev;
int ret = 0;

if (rdev->supply) {
ret = regulator_enable(rdev->supply);
if (ret != 0)
return ret;
}

mutex_lock(&rdev->mutex);
ret = _regulator_enable(rdev);
mutex_unlock(&rdev->mutex);

if (ret != 0)
regulator_disable(rdev->supply);

return ret;
}
EXPORT_SYMBOL_GPL(regulator_enable);

/* locks held by regulator_disable() */
static int _regulator_disable(struct regulator_dev *rdev,
struct regulator_dev **supply_rdev_ptr)
static int _regulator_disable(struct regulator_dev *rdev)
{
int ret = 0;
*supply_rdev_ptr = NULL;

if (WARN(rdev->use_count <= 0,
"unbalanced disables for %s\n", rdev_get_name(rdev)))
Expand All @@ -1429,9 +1425,6 @@ static int _regulator_disable(struct regulator_dev *rdev,
NULL);
}

/* decrease our supplies ref count and disable if required */
*supply_rdev_ptr = rdev->supply;

rdev->use_count = 0;
} else if (rdev->use_count > 1) {

Expand All @@ -1442,6 +1435,7 @@ static int _regulator_disable(struct regulator_dev *rdev,

rdev->use_count--;
}

return ret;
}

Expand All @@ -1460,29 +1454,21 @@ static int _regulator_disable(struct regulator_dev *rdev,
int regulator_disable(struct regulator *regulator)
{
struct regulator_dev *rdev = regulator->rdev;
struct regulator_dev *supply_rdev = NULL;
int ret = 0;

mutex_lock(&rdev->mutex);
ret = _regulator_disable(rdev, &supply_rdev);
ret = _regulator_disable(rdev);
mutex_unlock(&rdev->mutex);

/* decrease our supplies ref count and disable if required */
while (supply_rdev != NULL) {
rdev = supply_rdev;

mutex_lock(&rdev->mutex);
_regulator_disable(rdev, &supply_rdev);
mutex_unlock(&rdev->mutex);
}
if (ret == 0 && rdev->supply)
regulator_disable(rdev->supply);

return ret;
}
EXPORT_SYMBOL_GPL(regulator_disable);

/* locks held by regulator_force_disable() */
static int _regulator_force_disable(struct regulator_dev *rdev,
struct regulator_dev **supply_rdev_ptr)
static int _regulator_force_disable(struct regulator_dev *rdev)
{
int ret = 0;

Expand All @@ -1499,10 +1485,6 @@ static int _regulator_force_disable(struct regulator_dev *rdev,
REGULATOR_EVENT_DISABLE, NULL);
}

/* decrease our supplies ref count and disable if required */
*supply_rdev_ptr = rdev->supply;

rdev->use_count = 0;
return ret;
}

Expand All @@ -1518,16 +1500,16 @@ static int _regulator_force_disable(struct regulator_dev *rdev,
int regulator_force_disable(struct regulator *regulator)
{
struct regulator_dev *rdev = regulator->rdev;
struct regulator_dev *supply_rdev = NULL;
int ret;

mutex_lock(&rdev->mutex);
regulator->uA_load = 0;
ret = _regulator_force_disable(rdev, &supply_rdev);
ret = _regulator_force_disable(regulator->rdev);
mutex_unlock(&rdev->mutex);

if (supply_rdev)
regulator_disable(get_device_regulator(rdev_get_dev(supply_rdev)));
if (rdev->supply)
while (rdev->open_count--)
regulator_disable(rdev->supply);

return ret;
}
Expand Down Expand Up @@ -2138,7 +2120,7 @@ int regulator_set_optimum_mode(struct regulator *regulator, int uA_load)
/* get input voltage */
input_uV = 0;
if (rdev->supply)
input_uV = _regulator_get_voltage(rdev->supply);
input_uV = regulator_get_voltage(rdev->supply);
if (input_uV <= 0)
input_uV = rdev->constraints->input_uV;
if (input_uV <= 0) {
Expand Down Expand Up @@ -2208,17 +2190,8 @@ EXPORT_SYMBOL_GPL(regulator_unregister_notifier);
static void _notifier_call_chain(struct regulator_dev *rdev,
unsigned long event, void *data)
{
struct regulator_dev *_rdev;

/* call rdev chain first */
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);
}
}

/**
Expand Down Expand Up @@ -2610,9 +2583,7 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc,
rdev->owner = regulator_desc->owner;
rdev->desc = regulator_desc;
INIT_LIST_HEAD(&rdev->consumer_list);
INIT_LIST_HEAD(&rdev->supply_list);
INIT_LIST_HEAD(&rdev->list);
INIT_LIST_HEAD(&rdev->slist);
BLOCKING_INIT_NOTIFIER_HEAD(&rdev->notifier);

/* preform any regulator specific init */
Expand Down Expand Up @@ -2724,7 +2695,7 @@ void regulator_unregister(struct regulator_dev *rdev)
unset_regulator_supplies(rdev);
list_del(&rdev->list);
if (rdev->supply)
sysfs_remove_link(&rdev->dev.kobj, "supply");
regulator_put(rdev->supply);
device_unregister(&rdev->dev);
kfree(rdev->constraints);
mutex_unlock(&regulator_list_mutex);
Expand Down
4 changes: 1 addition & 3 deletions include/linux/regulator/driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -188,18 +188,16 @@ struct regulator_dev {

/* lists we belong to */
struct list_head list; /* list of all regulators */
struct list_head slist; /* list of supplied regulators */

/* lists we own */
struct list_head consumer_list; /* consumers we supply */
struct list_head supply_list; /* regulators we supply */

struct blocking_notifier_head notifier;
struct mutex mutex; /* consumer lock */
struct module *owner;
struct device dev;
struct regulation_constraints *constraints;
struct regulator_dev *supply; /* for tree */
struct regulator *supply; /* for tree */

void *reg_data; /* regulator_dev data */

Expand Down

0 comments on commit 3801b86

Please sign in to comment.