diff options
author | Benn Pörscke <benn.porscke@stericsson.com> | 2011-10-07 15:31:57 +0200 |
---|---|---|
committer | Benn Pörscke <benn.porscke@stericsson.com> | 2011-10-07 15:31:57 +0200 |
commit | 47a4dbf83a75014d6b3467be18997894f1c617db (patch) | |
tree | 7f5d116db48205309fbc4ae0954f20ab8a651e46 /drivers/regulator/core.c | |
parent | ea8a52f9f4bcc3420c38ae07f8378a2f18443970 (diff) |
Squashandroid-20111012
Change-Id: If0ae9fa8067740ab2ede33703c79ec134f204a5e
Diffstat (limited to 'drivers/regulator/core.c')
-rw-r--r-- | drivers/regulator/core.c | 109 |
1 files changed, 85 insertions, 24 deletions
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 2248087b9be..0d78dfa7476 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -60,15 +60,17 @@ struct regulator { char *supply_name; struct device_attribute dev_attr; struct regulator_dev *rdev; + int use; }; 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_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); + unsigned long event, void *data, int lock_sublevel); static const char *rdev_get_name(struct regulator_dev *rdev) { @@ -517,6 +519,32 @@ static ssize_t regulator_suspend_standby_state_show(struct device *dev, static DEVICE_ATTR(suspend_standby_state, 0444, regulator_suspend_standby_state_show, NULL); +static ssize_t regulator_use_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = dev_get_drvdata(dev); + struct regulator *reg; + size_t size = 0; + + if (rdev->use_count == 0) + return sprintf(buf, "no users\n"); + + list_for_each_entry(reg, &rdev->consumer_list, list) { + if (!reg->use) + continue; + + if (reg->dev != NULL) + size += sprintf((buf + size), "%s (%d) ", + dev_name(reg->dev), reg->use); + else + size += sprintf((buf + size), "unknown (%d) ", + reg->use); + } + size += sprintf((buf + size), "\n"); + + return size; +} +static DEVICE_ATTR(use, 0444, regulator_use_show, NULL); /* * These are the only attributes are present for all regulators. @@ -1262,13 +1290,15 @@ static int _regulator_enable(struct regulator_dev *rdev) { int ret, delay; - /* do we need to enable the supply regulator first */ - if (rdev->supply) { - ret = _regulator_enable(rdev->supply); - if (ret < 0) { - printk(KERN_ERR "%s: failed to enable %s: %d\n", - __func__, rdev_get_name(rdev), ret); - return ret; + if (rdev->use_count == 0) { + /* do we need to enable the supply regulator first */ + if (rdev->supply) { + ret = _regulator_enable(rdev->supply); + if (ret < 0) { + printk(KERN_ERR "%s: failed to enable %s: %d\n", + __func__, rdev_get_name(rdev), ret); + return ret; + } } } @@ -1344,14 +1374,19 @@ int regulator_enable(struct regulator *regulator) mutex_lock(&rdev->mutex); ret = _regulator_enable(rdev); mutex_unlock(&rdev->mutex); + if (ret == 0) + regulator->use++; + return ret; } EXPORT_SYMBOL_GPL(regulator_enable); /* 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; + *supply_rdev_ptr = NULL; if (WARN(rdev->use_count <= 0, "unbalanced disables for %s\n", @@ -1373,12 +1408,11 @@ static int _regulator_disable(struct regulator_dev *rdev) } _notifier_call_chain(rdev, REGULATOR_EVENT_DISABLE, - NULL); + NULL, 0); } /* decrease our supplies ref count and disable if required */ - if (rdev->supply) - _regulator_disable(rdev->supply); + *supply_rdev_ptr = rdev->supply; rdev->use_count = 0; } else if (rdev->use_count > 1) { @@ -1408,17 +1442,32 @@ 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); + ret = _regulator_disable(rdev, &supply_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) + regulator->use--; + return ret; } EXPORT_SYMBOL_GPL(regulator_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; @@ -1433,12 +1482,11 @@ static int _regulator_force_disable(struct regulator_dev *rdev) } /* notify other consumers that power has been forced off */ _notifier_call_chain(rdev, REGULATOR_EVENT_FORCE_DISABLE | - REGULATOR_EVENT_DISABLE, NULL); + REGULATOR_EVENT_DISABLE, NULL, 0); } /* decrease our supplies ref count and disable if required */ - if (rdev->supply) - _regulator_disable(rdev->supply); + *supply_rdev_ptr = rdev->supply; rdev->use_count = 0; return ret; @@ -1455,12 +1503,17 @@ static int _regulator_force_disable(struct regulator_dev *rdev) */ int regulator_force_disable(struct regulator *regulator) { + struct regulator_dev *supply_rdev = NULL; int ret; mutex_lock(®ulator->rdev->mutex); regulator->uA_load = 0; - ret = _regulator_force_disable(regulator->rdev); + ret = _regulator_force_disable(regulator->rdev, &supply_rdev); mutex_unlock(®ulator->rdev->mutex); + + if (supply_rdev) + regulator_disable(get_device_regulator(rdev_get_dev(supply_rdev))); + return ret; } EXPORT_SYMBOL_GPL(regulator_force_disable); @@ -1617,7 +1670,7 @@ int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV) ret = rdev->desc->ops->set_voltage(rdev, min_uV, max_uV); out: - _notifier_call_chain(rdev, REGULATOR_EVENT_VOLTAGE_CHANGE, NULL); + _notifier_call_chain(rdev, REGULATOR_EVENT_VOLTAGE_CHANGE, NULL, 0); mutex_unlock(&rdev->mutex); return ret; } @@ -1930,19 +1983,23 @@ EXPORT_SYMBOL_GPL(regulator_unregister_notifier); /* notify regulator consumers and downstream regulator consumers. * Note mutex must be held by caller. + * lock_sublevel should always be 0, only used for recursive calls. */ static void _notifier_call_chain(struct regulator_dev *rdev, - unsigned long event, void *data) + unsigned long event, void *data, int lock_sublevel) { struct regulator_dev *_rdev; /* call rdev chain first */ blocking_notifier_call_chain(&rdev->notifier, event, NULL); + /* increase sublevel before stepping into nested regulators */ + lock_sublevel++; + /* 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_lock_nested(&_rdev->mutex, lock_sublevel); + _notifier_call_chain(_rdev, event, data, lock_sublevel); mutex_unlock(&_rdev->mutex); } } @@ -2097,7 +2154,7 @@ EXPORT_SYMBOL_GPL(regulator_bulk_free); int regulator_notifier_call_chain(struct regulator_dev *rdev, unsigned long event, void *data) { - _notifier_call_chain(rdev, event, data); + _notifier_call_chain(rdev, event, data, 0); return NOTIFY_DONE; } @@ -2137,6 +2194,10 @@ static int add_regulator_attributes(struct regulator_dev *rdev) struct regulator_ops *ops = rdev->desc->ops; int status = 0; + status = device_create_file(dev, &dev_attr_use); + if (status < 0) + dev_warn(dev, "Create sysfs file \"use\" failed"); + /* some attributes need specific methods to be displayed */ if (ops->get_voltage) { status = device_create_file(dev, &dev_attr_microvolts); |