#define CREATE_TRACE_POINTS
#include <trace/events/regulator.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
#include "dummy.h"
static DEFINE_MUTEX(regulator_list_mutex);
static LIST_HEAD(regulator_list);
static LIST_HEAD(regulator_map_list);
+static LIST_HEAD(regulator_ena_gpio_list);
static bool has_full_constraints;
static bool board_wants_dummy_regulator;
struct regulator_dev *regulator;
};
+/*
+ * struct regulator_enable_gpio
+ *
+ * Management for shared enable GPIO pin
+ */
+struct regulator_enable_gpio {
+ struct list_head list;
+ int gpio;
+ u32 enable_count; /* a number of enabled shared GPIO */
+ u32 request_count; /* a number of requested shared GPIO */
+ unsigned int ena_gpio_invert:1;
+};
+
/*
* struct regulator
*
static int _regulator_is_enabled(struct regulator_dev *rdev);
static int _regulator_disable(struct regulator_dev *rdev);
+static int _regulator_enable(struct regulator_dev *rdev);
+static int _regulator_get_enable_time(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);
* @supply: regulator supply name
*
* Extract the regulator device node corresponding to the supply name.
- * retruns the device node corresponding to the regulator if found, else
+ * returns the device node corresponding to the regulator if found, else
* returns NULL.
*/
static struct device_node *of_get_regulator(struct device *dev, const char *supply)
*min_uV = regulator->min_uV;
}
- if (*min_uV > *max_uV)
+ if (*min_uV > *max_uV) {
+ rdev_err(rdev, "Restricting voltage, %u-%uuV\n",
+ *min_uV, *max_uV);
return -EINVAL;
+ }
return 0;
}
return 0;
}
+/* dynamic regulator control mode switching constraint check */
+static int regulator_check_control(struct regulator_dev *rdev)
+{
+ if (!rdev->constraints) {
+ rdev_err(rdev, "no constraints\n");
+ return -ENODEV;
+ }
+ if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_CONTROL)) {
+ rdev_err(rdev, "operation not allowed\n");
+ return -EPERM;
+ }
+ return 0;
+}
+
+static ssize_t regulator_uV_set(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct regulator_dev *rdev = dev_get_drvdata(dev);
+ int ret;
+ int min_uV;
+ int max_uV = rdev->constraints->max_uV;
+ char *p = (char *)buf;
+
+ min_uV = memparse(p, &p);
+ mutex_lock(&rdev->mutex);
+
+ /* sanity check */
+ if (!rdev->desc->ops->set_voltage &&
+ !rdev->desc->ops->set_voltage_sel) {
+ rdev_err(rdev, "The operation is not supported\n");
+ goto out;
+ }
+
+ /* constraints check */
+ ret = regulator_check_voltage(rdev, &min_uV, &max_uV);
+ if (ret < 0) {
+ rdev_err(rdev, "Voltage is out of range min:max= %d:%d\n",
+ rdev->constraints->min_uV, rdev->constraints->max_uV);
+ goto out;
+ }
+
+ /* Consumer check */
+ ret = regulator_check_consumers(rdev, &min_uV, &max_uV);
+ if (ret < 0) {
+ rdev_warn(rdev, "new voltage is out-range for some consumer\n");
+ rdev_warn(rdev, "min: max = %d:%d\n", min_uV, max_uV);
+ }
+
+ rdev_info(rdev, "Setting voltage min:max = %d:%d\n", min_uV, max_uV);
+ ret = _regulator_do_set_voltage(rdev, min_uV, max_uV);
+ if (ret < 0)
+ rdev_warn(rdev, "Can not set voltage %d:%d\n", min_uV, max_uV);
+
+out:
+ mutex_unlock(&rdev->mutex);
+ return count;
+}
+
static ssize_t regulator_uV_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return ret;
}
-static DEVICE_ATTR(microvolts, 0444, regulator_uV_show, NULL);
+static DEVICE_ATTR(microvolts, 0644, regulator_uV_show, regulator_uV_set);
static ssize_t regulator_uA_show(struct device *dev,
struct device_attribute *attr, char *buf)
return ret;
}
-static DEVICE_ATTR(state, 0444, regulator_state_show, NULL);
+
+static ssize_t regulator_state_set(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct regulator_dev *rdev = dev_get_drvdata(dev);
+ int ret;
+ bool enabled;
+
+ if ((*buf == 'E') || (*buf == 'e'))
+ enabled = true;
+ else if ((*buf == 'D') || (*buf == 'd'))
+ enabled = false;
+ else
+ return -EINVAL;
+
+ if ((_regulator_is_enabled(rdev) && enabled) ||
+ (!_regulator_is_enabled(rdev) && !enabled))
+ return count;
+
+ mutex_lock(&rdev->mutex);
+ if (enabled) {
+ int delay = 0;
+ if (!rdev->desc->ops->enable) {
+ ret = -EINVAL;
+ goto end;
+ }
+ ret = _regulator_get_enable_time(rdev);
+ if (ret >= 0)
+ delay = ret;
+ ret = rdev->desc->ops->enable(rdev);
+ if (ret < 0) {
+ rdev_warn(rdev, "enable() failed: %d\n", ret);
+ goto end;
+ }
+ if (delay >= 1000) {
+ mdelay(delay / 1000);
+ udelay(delay % 1000);
+ } else if (delay) {
+ udelay(delay);
+ }
+ } else {
+ if (!rdev->desc->ops->disable) {
+ ret = -EINVAL;
+ goto end;
+ }
+ ret = rdev->desc->ops->disable(rdev);
+ if (ret < 0) {
+ rdev_warn(rdev, "disable() failed: %d\n", ret);
+ goto end;
+ }
+ }
+
+end:
+ mutex_unlock(&rdev->mutex);
+ if (ret < 0)
+ return ret;
+ return count;
+}
+static DEVICE_ATTR(state, 0644, regulator_state_show, regulator_state_set);
static ssize_t regulator_status_show(struct device *dev,
struct device_attribute *attr, char *buf)
}
}
+ if (rdev->constraints->init_uV) {
+ ret = _regulator_do_set_voltage(rdev,
+ rdev->constraints->init_uV,
+ rdev->constraints->init_uV);
+ if (ret < 0) {
+ rdev_err(rdev, "failed to set init %duV constraint\n",
+ rdev->constraints->init_uV);
+ return ret;
+ }
+
+ rdev_info(rdev, "applied init %duV constraint\n",
+ rdev->constraints->init_uV);
+ }
+
/* constrain machine-level voltage specs to fit
* the actual range supported by this regulator.
*/
/* final: [min_uV..max_uV] valid iff constraints valid */
if (max_uV < min_uV) {
- rdev_err(rdev, "unsupportable voltage constraints\n");
+ rdev_err(rdev,
+ "unsupportable voltage constraints %u-%uuV\n",
+ min_uV, max_uV);
return -EINVAL;
}
}
}
+ if (rdev->constraints->sleep_mode) {
+ if (!ops->set_sleep_mode) {
+ rdev_err(rdev, "no set_sleep_mode operation\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = ops->set_sleep_mode(rdev, rdev->constraints->sleep_mode);
+ if (ret < 0) {
+ rdev_err(rdev, "failed to set sleep mode: %d\n", ret);
+ goto out;
+ }
+ }
+
/* If the constraints say the regulator should be on at this point
* and we have control then make sure it is enabled.
*/
rdev_err(rdev, "failed to enable\n");
goto out;
}
+
+ ret = _regulator_get_enable_time(rdev);
+ if (ret > 0) {
+ if (ret >= 1000) {
+ mdelay(ret / 1000);
+ udelay(ret % 1000);
+ } else
+ udelay(ret);
+ }
+ }
+
+ if ((rdev->constraints->boot_off) && ops->disable) {
+ ret = ops->disable(rdev);
+ if (ret < 0) {
+ rdev_err(rdev, "failed to disable\n");
+ goto out;
+ }
}
if (rdev->constraints->ramp_delay && ops->set_ramp_delay) {
struct regulator_dev *rdev;
struct regulator *regulator = ERR_PTR(-EPROBE_DEFER);
const char *devname = NULL;
- int ret;
+ int ret = 0;
if (id == NULL) {
pr_err("get() with no identifier\n");
if (rdev)
goto found;
+ /*
+ * If we have return value from dev_lookup fail, we do not expect to
+ * succeed, so, quit with appropriate error value
+ */
+ if (ret) {
+ regulator = ERR_PTR(ret);
+ goto out;
+ }
+
if (board_wants_dummy_regulator) {
rdev = dummy_regulator_rdev;
goto found;
}
EXPORT_SYMBOL_GPL(regulator_get_exclusive);
-/**
- * regulator_put - "free" the regulator source
- * @regulator: regulator source
- *
- * Note: drivers must ensure that all regulator_enable calls made on this
- * regulator source are balanced by regulator_disable calls prior to calling
- * this function.
- */
-void regulator_put(struct regulator *regulator)
+/* Locks held by regulator_put() */
+static void _regulator_put(struct regulator *regulator)
{
struct regulator_dev *rdev;
if (regulator == NULL || IS_ERR(regulator))
return;
- mutex_lock(®ulator_list_mutex);
rdev = regulator->rdev;
debugfs_remove_recursive(regulator->debugfs);
rdev->exclusive = 0;
module_put(rdev->owner);
+}
+
+/**
+ * regulator_put - "free" the regulator source
+ * @regulator: regulator source
+ *
+ * Note: drivers must ensure that all regulator_enable calls made on this
+ * regulator source are balanced by regulator_disable calls prior to calling
+ * this function.
+ */
+void regulator_put(struct regulator *regulator)
+{
+ mutex_lock(®ulator_list_mutex);
+ _regulator_put(regulator);
mutex_unlock(®ulator_list_mutex);
}
EXPORT_SYMBOL_GPL(regulator_put);
}
EXPORT_SYMBOL_GPL(devm_regulator_put);
+/* Manage enable GPIO list. Same GPIO pin can be shared among regulators */
+static int regulator_ena_gpio_request(struct regulator_dev *rdev,
+ const struct regulator_config *config)
+{
+ struct regulator_enable_gpio *pin;
+ int ret;
+
+ list_for_each_entry(pin, ®ulator_ena_gpio_list, list) {
+ if (pin->gpio == config->ena_gpio) {
+ rdev_dbg(rdev, "GPIO %d is already used\n",
+ config->ena_gpio);
+ goto update_ena_gpio_to_rdev;
+ }
+ }
+
+ ret = gpio_request_one(config->ena_gpio,
+ GPIOF_DIR_OUT | config->ena_gpio_flags,
+ rdev_get_name(rdev));
+ if (ret)
+ return ret;
+
+ pin = kzalloc(sizeof(struct regulator_enable_gpio), GFP_KERNEL);
+ if (pin == NULL) {
+ gpio_free(config->ena_gpio);
+ return -ENOMEM;
+ }
+
+ pin->gpio = config->ena_gpio;
+ pin->ena_gpio_invert = config->ena_gpio_invert;
+ list_add(&pin->list, ®ulator_ena_gpio_list);
+
+update_ena_gpio_to_rdev:
+ pin->request_count++;
+ rdev->ena_pin = pin;
+ return 0;
+}
+
+static void regulator_ena_gpio_free(struct regulator_dev *rdev)
+{
+ struct regulator_enable_gpio *pin, *n;
+
+ if (!rdev->ena_pin)
+ return;
+
+ /* Free the GPIO only in case of no use */
+ list_for_each_entry_safe(pin, n, ®ulator_ena_gpio_list, list) {
+ if (pin->gpio == rdev->ena_pin->gpio) {
+ if (pin->request_count <= 1) {
+ pin->request_count = 0;
+ gpio_free(pin->gpio);
+ list_del(&pin->list);
+ kfree(pin);
+ } else {
+ pin->request_count--;
+ }
+ }
+ }
+}
+
+/**
+ * regulator_ena_gpio_ctrl - balance enable_count of each GPIO and actual GPIO pin control
+ * @rdev: regulator_dev structure
+ * @enable: enable GPIO at initial use?
+ *
+ * GPIO is enabled in case of initial use. (enable_count is 0)
+ * GPIO is disabled when it is not shared any more. (enable_count <= 1)
+ */
+static int regulator_ena_gpio_ctrl(struct regulator_dev *rdev, bool enable)
+{
+ struct regulator_enable_gpio *pin = rdev->ena_pin;
+
+ if (!pin)
+ return -EINVAL;
+
+ if (enable) {
+ /* Enable GPIO at initial use */
+ if (pin->enable_count == 0)
+ gpio_set_value_cansleep(pin->gpio,
+ !pin->ena_gpio_invert);
+
+ pin->enable_count++;
+ } else {
+ if (pin->enable_count > 1) {
+ pin->enable_count--;
+ return 0;
+ }
+
+ /* Disable GPIO if not used */
+ if (pin->enable_count <= 1) {
+ gpio_set_value_cansleep(pin->gpio,
+ pin->ena_gpio_invert);
+ pin->enable_count = 0;
+ }
+ }
+
+ return 0;
+}
+
static int _regulator_do_enable(struct regulator_dev *rdev)
{
int ret, delay;
}
trace_regulator_enable(rdev_get_name(rdev));
+ _notifier_call_chain(rdev, REGULATOR_EVENT_PRE_ENABLE, NULL);
- if (rdev->ena_gpio) {
- gpio_set_value_cansleep(rdev->ena_gpio,
- !rdev->ena_gpio_invert);
+ if (rdev->ena_pin) {
+ ret = regulator_ena_gpio_ctrl(rdev, true);
+ if (ret < 0)
+ return ret;
rdev->ena_gpio_state = 1;
} else if (rdev->desc->ops->enable) {
ret = rdev->desc->ops->enable(rdev);
udelay(delay);
}
+ _notifier_call_chain(rdev, REGULATOR_EVENT_POST_ENABLE, NULL);
trace_regulator_enable_complete(rdev_get_name(rdev));
return 0;
trace_regulator_disable(rdev_get_name(rdev));
- if (rdev->ena_gpio) {
- gpio_set_value_cansleep(rdev->ena_gpio,
- rdev->ena_gpio_invert);
+ if (rdev->ena_pin) {
+ ret = regulator_ena_gpio_ctrl(rdev, false);
+ if (ret < 0)
+ return ret;
rdev->ena_gpio_state = 0;
} else if (rdev->desc->ops->disable) {
if (ret != 0)
return ret;
- return (val & rdev->desc->enable_mask) != 0;
+ if (rdev->desc->enable_is_inverted)
+ return (val & rdev->desc->enable_mask) == 0;
+ else
+ return (val & rdev->desc->enable_mask) != 0;
}
EXPORT_SYMBOL_GPL(regulator_is_enabled_regmap);
*/
int regulator_enable_regmap(struct regulator_dev *rdev)
{
+ unsigned int val;
+
+ if (rdev->desc->enable_is_inverted)
+ val = 0;
+ else
+ val = rdev->desc->enable_mask;
+
return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
- rdev->desc->enable_mask,
- rdev->desc->enable_mask);
+ rdev->desc->enable_mask, val);
}
EXPORT_SYMBOL_GPL(regulator_enable_regmap);
*/
int regulator_disable_regmap(struct regulator_dev *rdev)
{
+ unsigned int val;
+
+ if (rdev->desc->enable_is_inverted)
+ val = rdev->desc->enable_mask;
+ else
+ val = 0;
+
return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
- rdev->desc->enable_mask, 0);
+ rdev->desc->enable_mask, val);
}
EXPORT_SYMBOL_GPL(regulator_disable_regmap);
static int _regulator_is_enabled(struct regulator_dev *rdev)
{
/* A GPIO control always takes precedence */
- if (rdev->ena_gpio)
+ if (rdev->ena_pin)
return rdev->ena_gpio_state;
/* If we don't know then assume that the regulator is always on */
}
EXPORT_SYMBOL_GPL(regulator_is_enabled);
+/**
+ * regulator_can_change_voltage - check if regulator can change voltage
+ * @regulator: regulator source
+ *
+ * Returns positive if the regulator driver backing the source/client
+ * can change its voltage, false otherwise. Usefull for detecting fixed
+ * or dummy regulators and disabling voltage change logic in the client
+ * driver.
+ */
+int regulator_can_change_voltage(struct regulator *regulator)
+{
+ struct regulator_dev *rdev = regulator->rdev;
+
+ if (rdev->constraints &&
+ (rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_VOLTAGE)) {
+ if (rdev->desc->n_voltages - rdev->desc->linear_min_sel > 1)
+ return 1;
+
+ if (rdev->desc->continuous_voltage_range &&
+ rdev->constraints->min_uV && rdev->constraints->max_uV &&
+ rdev->constraints->min_uV != rdev->constraints->max_uV)
+ return 1;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(regulator_can_change_voltage);
+
/**
* regulator_count_voltages - count regulator_list_voltage() selectors
* @regulator: regulator source
{
if (selector >= rdev->desc->n_voltages)
return -EINVAL;
+ if (selector < rdev->desc->linear_min_sel)
+ return 0;
+
+ selector -= rdev->desc->linear_min_sel;
return rdev->desc->min_uV + (rdev->desc->uV_step * selector);
}
if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_VOLTAGE)) {
ret = regulator_get_voltage(regulator);
if (ret >= 0)
- return (min_uV >= ret && ret <= max_uV);
+ return (min_uV <= ret && ret <= max_uV);
else
return ret;
}
+ /* Any voltage within constrains range is fine? */
+ if (rdev->desc->continuous_voltage_range)
+ return min_uV >= rdev->constraints->min_uV &&
+ max_uV <= rdev->constraints->max_uV;
+
ret = regulator_count_voltages(regulator);
if (ret < 0)
return ret;
*/
int regulator_set_voltage_sel_regmap(struct regulator_dev *rdev, unsigned sel)
{
+ int ret;
+
sel <<= ffs(rdev->desc->vsel_mask) - 1;
- return regmap_update_bits(rdev->regmap, rdev->desc->vsel_reg,
+ ret = regmap_update_bits(rdev->regmap, rdev->desc->vsel_reg,
rdev->desc->vsel_mask, sel);
+ if (ret)
+ return ret;
+
+ if (rdev->desc->apply_bit)
+ ret = regmap_update_bits(rdev->regmap, rdev->desc->apply_reg,
+ rdev->desc->apply_bit,
+ rdev->desc->apply_bit);
+ return ret;
}
EXPORT_SYMBOL_GPL(regulator_set_voltage_sel_regmap);
}
EXPORT_SYMBOL_GPL(regulator_map_voltage_iterate);
+/**
+ * regulator_map_voltage_ascend - map_voltage() for ascendant voltage list
+ *
+ * @rdev: Regulator to operate on
+ * @min_uV: Lower bound for voltage
+ * @max_uV: Upper bound for voltage
+ *
+ * Drivers that have ascendant voltage list can use this as their
+ * map_voltage() operation.
+ */
+int regulator_map_voltage_ascend(struct regulator_dev *rdev,
+ int min_uV, int max_uV)
+{
+ int i, ret;
+
+ for (i = 0; i < rdev->desc->n_voltages; i++) {
+ ret = rdev->desc->ops->list_voltage(rdev, i);
+ if (ret < 0)
+ continue;
+
+ if (ret > max_uV)
+ break;
+
+ if (ret >= min_uV && ret <= max_uV)
+ return i;
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(regulator_map_voltage_ascend);
+
/**
* regulator_map_voltage_linear - map_voltage() for simple linear mappings
*
if (ret < 0)
return ret;
+ ret += rdev->desc->linear_min_sel;
+
/* Map back into a voltage to verify we're still in bounds */
voltage = rdev->desc->ops->list_voltage(rdev, ret);
if (voltage < min_uV || voltage > max_uV)
}
if (rdev->desc->ops->set_voltage) {
+ if (_regulator_is_enabled(rdev))
+ _notifier_call_chain(rdev,
+ REGULATOR_EVENT_OUT_PRECHANGE, (void *)min_uV);
+
ret = rdev->desc->ops->set_voltage(rdev, min_uV, max_uV,
&selector);
if (ret >= 0) {
best_val = rdev->desc->ops->list_voltage(rdev, ret);
if (min_uV <= best_val && max_uV >= best_val) {
+ if (_regulator_is_enabled(rdev))
+ _notifier_call_chain(rdev,
+ REGULATOR_EVENT_OUT_PRECHANGE, (void *)ret);
selector = ret;
- ret = rdev->desc->ops->set_voltage_sel(rdev,
- ret);
+ if (old_selector == selector)
+ ret = 0;
+ else
+ ret = rdev->desc->ops->set_voltage_sel(
+ rdev, ret);
} else {
ret = -EINVAL;
}
/* Call set_voltage_time_sel if successfully obtained old_selector */
if (ret == 0 && _regulator_is_enabled(rdev) && old_selector >= 0 &&
- rdev->desc->ops->set_voltage_time_sel) {
+ old_selector != selector && rdev->desc->ops->set_voltage_time_sel) {
delay = rdev->desc->ops->set_voltage_time_sel(rdev,
old_selector, selector);
(void *)data);
}
+ if (_regulator_is_enabled(rdev)) {
+ if (selector != -1)
+ min_uV = selector;
+ _notifier_call_chain(rdev, REGULATOR_EVENT_OUT_POSTCHANGE,
+ (void *)min_uV);
+ }
+
trace_regulator_set_voltage_complete(rdev_get_name(rdev), best_val);
return ret;
{
struct regulator_dev *rdev = regulator->rdev;
int ret = 0;
+ int old_min_uV, old_max_uV;
+
+#ifdef CONFIG_REGULATOR_DUMMY
+ if (!strcmp(rdev->desc->name, "dummy")) {
+ rdev_info(rdev,
+ "regulator is dummy, skipping voltage change...\n");
+ return ret;
+ }
+#endif
mutex_lock(&rdev->mutex);
ret = regulator_check_voltage(rdev, &min_uV, &max_uV);
if (ret < 0)
goto out;
+
+ /* restore original values in case of error */
+ old_min_uV = regulator->min_uV;
+ old_max_uV = regulator->max_uV;
regulator->min_uV = min_uV;
regulator->max_uV = max_uV;
ret = regulator_check_consumers(rdev, &min_uV, &max_uV);
if (ret < 0)
- goto out;
+ goto out2;
ret = _regulator_do_set_voltage(rdev, min_uV, max_uV);
-
+ if (ret < 0)
+ goto out2;
+
out:
mutex_unlock(&rdev->mutex);
return ret;
+out2:
+ regulator->min_uV = old_min_uV;
+ regulator->max_uV = old_max_uV;
+ mutex_unlock(&rdev->mutex);
+ return ret;
}
EXPORT_SYMBOL_GPL(regulator_set_voltage);
/**
* regulator_set_current_limit - set regulator output current limit
* @regulator: regulator source
- * @min_uA: Minimuum supported current in uA
+ * @min_uA: Minimum supported current in uA
* @max_uA: Maximum supported current in uA
*
* Sets current sink to the desired output current. This can be set during
* regulator_allow_bypass - allow the regulator to go into bypass mode
*
* @regulator: Regulator to configure
- * @allow: enable or disable bypass mode
+ * @enable: enable or disable bypass mode
*
* Allow the regulator to go into bypass mode if all other consumers
* for the regulator also enable bypass mode and the machine
}
EXPORT_SYMBOL_GPL(regulator_allow_bypass);
+/*
+ * regulator_set_control_mode - set regulator control mode
+ * @regulator: regulator source
+ * @mode: control mode - one of the REGULATOR_CONTROL_MODE constants
+ *
+ * Set regulator control mode to regulate the regulator output.
+ *
+ * NOTE: Regulator system constraints must be set for this regulator before
+ * calling this function otherwise this call will fail.
+ */
+int regulator_set_control_mode(struct regulator *regulator, unsigned int mode)
+{
+ struct regulator_dev *rdev = regulator->rdev;
+ int ret;
+ int regulator_curr_mode;
+
+ mutex_lock(&rdev->mutex);
+
+ /* sanity check */
+ if (!rdev->desc->ops->set_control_mode) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* return if the same mode is requested */
+ if (rdev->desc->ops->get_control_mode) {
+ regulator_curr_mode = rdev->desc->ops->get_control_mode(rdev);
+ if (regulator_curr_mode == mode) {
+ ret = 0;
+ goto out;
+ }
+ }
+
+ /* constraints check */
+ ret = regulator_check_control(rdev);
+ if (ret < 0)
+ goto out;
+
+ ret = rdev->desc->ops->set_control_mode(rdev, mode);
+out:
+ mutex_unlock(&rdev->mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(regulator_set_control_mode);
+
+/**
+ * regulator_get_control_mode - get regulator control mode
+ * @regulator: regulator source
+ *
+ * Get the current regulator control mode.
+ */
+unsigned int regulator_get_control_mode(struct regulator *regulator)
+{
+ struct regulator_dev *rdev = regulator->rdev;
+ int ret = -EINVAL;
+
+ mutex_lock(&rdev->mutex);
+
+ /* sanity check */
+ if (!rdev->desc->ops->get_control_mode)
+ goto out;
+
+ ret = rdev->desc->ops->get_control_mode(rdev);
+out:
+ mutex_unlock(&rdev->mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(regulator_get_control_mode);
+
+/**
+ * regulator_set_ramp_delay - set regulator ramp delay
+ * @regulator: regulator source
+ * @ramp_delay_uV: Ramp delay uV/us.
+ *
+ * Set regulator ramp delay if regulator driver provided the
+ * callback API.
+ */
+int regulator_set_ramp_delay(struct regulator *regulator, int ramp_delay_uV)
+{
+ struct regulator_dev *rdev = regulator->rdev;
+ int ret;
+
+ mutex_lock(&rdev->mutex);
+
+ /* sanity check */
+ if (!rdev->desc->ops->set_ramp_delay) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = rdev->desc->ops->set_ramp_delay(rdev, ramp_delay_uV);
+out:
+ mutex_unlock(&rdev->mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(regulator_set_ramp_delay);
+
/**
* regulator_register_notifier - register regulator event notifier
* @regulator: regulator source
return 0;
err:
- pr_err("Failed to enable %s: %d\n", consumers[i].supply, ret);
- while (--i >= 0)
- regulator_disable(consumers[i].consumer);
+ for (i = 0; i < num_consumers; i++) {
+ if (consumers[i].ret < 0)
+ pr_err("Failed to enable %s: %d\n", consumers[i].supply,
+ consumers[i].ret);
+ else
+ regulator_disable(consumers[i].consumer);
+ }
return ret;
}
if (status < 0)
return status;
}
- if (ops->is_enabled) {
+ if (rdev->ena_pin || ops->is_enabled) {
status = device_create_file(dev, &dev_attr_state);
if (status < 0)
return status;
* @config: runtime configuration for regulator
*
* Called by regulator drivers to register a regulator.
- * Returns 0 on success.
+ * Returns a valid pointer to struct regulator_dev on success
+ * or an ERR_PTR() on error.
*/
struct regulator_dev *
regulator_register(const struct regulator_desc *regulator_desc,
struct device *dev;
int ret, i;
const char *supply = NULL;
+ bool parent_enable = false;
if (regulator_desc == NULL || config == NULL)
return ERR_PTR(-EINVAL);
dev_set_drvdata(&rdev->dev, rdev);
if (config->ena_gpio && gpio_is_valid(config->ena_gpio)) {
- ret = gpio_request_one(config->ena_gpio,
- GPIOF_DIR_OUT | config->ena_gpio_flags,
- rdev_get_name(rdev));
+ ret = regulator_ena_gpio_request(rdev, config);
if (ret != 0) {
rdev_err(rdev, "Failed to request enable GPIO%d: %d\n",
config->ena_gpio, ret);
- goto clean;
+ goto wash;
}
- rdev->ena_gpio = config->ena_gpio;
- rdev->ena_gpio_invert = config->ena_gpio_invert;
-
if (config->ena_gpio_flags & GPIOF_OUT_INIT_HIGH)
rdev->ena_gpio_state = 1;
- if (rdev->ena_gpio_invert)
+ if (config->ena_gpio_invert)
rdev->ena_gpio_state = !rdev->ena_gpio_state;
}
if (init_data && init_data->supply_regulator)
supply = init_data->supply_regulator;
+#if 0 /* Reenable when EPROBE_DEFER is pulled. */
else if (regulator_desc->supply_name)
supply = regulator_desc->supply_name;
+#endif
if (supply) {
struct regulator_dev *r;
r = regulator_dev_lookup(dev, supply, &ret);
- if (!r) {
+ if (ret == -ENODEV) {
+ /*
+ * No supply was specified for this regulator and
+ * there will never be one.
+ */
+ ret = 0;
+ goto add_dev;
+ } else if (!r) {
dev_err(dev, "Failed to find supply %s\n", supply);
ret = -EPROBE_DEFER;
goto scrub;
ret = regulator_enable(rdev->supply);
if (ret < 0)
goto scrub;
+ parent_enable = true;
}
}
+add_dev:
/* add consumers devices */
if (init_data) {
for (i = 0; i < init_data->num_consumer_supplies; i++) {
unset_supplies:
unset_regulator_supplies(rdev);
+ if (rdev->supply && parent_enable)
+ regulator_disable(rdev->supply);
scrub:
if (rdev->supply)
- regulator_put(rdev->supply);
- if (rdev->ena_gpio)
- gpio_free(rdev->ena_gpio);
+ _regulator_put(rdev->supply);
+ regulator_ena_gpio_free(rdev);
kfree(rdev->constraints);
+wash:
device_unregister(&rdev->dev);
/* device core frees rdev */
rdev = ERR_PTR(ret);
unset_regulator_supplies(rdev);
list_del(&rdev->list);
kfree(rdev->constraints);
- if (rdev->ena_gpio)
- gpio_free(rdev->ena_gpio);
+ regulator_ena_gpio_free(rdev);
device_unregister(&rdev->dev);
mutex_unlock(®ulator_list_mutex);
}
struct regulation_constraints *c;
int enabled, ret;
- /*
- * Since DT doesn't provide an idiomatic mechanism for
- * enabling full constraints and since it's much more natural
- * with DT to provide them just assume that a DT enabled
- * system has full constraints.
- */
- if (of_have_populated_dt())
- has_full_constraints = true;
-
mutex_lock(®ulator_list_mutex);
/* If we have a full configuration then disable any regulators
* default behaviour in the future.
*/
list_for_each_entry(rdev, ®ulator_list, list) {
+ bool parent_disable = false;
+
ops = rdev->desc->ops;
c = rdev->constraints;
if (ret != 0) {
rdev_err(rdev, "couldn't disable: %d\n", ret);
}
+ if (ops->is_enabled)
+ parent_disable = true;
} else {
/* The intention is that in future we will
* assume that full constraints are provided
unlock:
mutex_unlock(&rdev->mutex);
+ if (parent_disable && rdev->supply) {
+ ret = regulator_disable(rdev->supply);
+ if (ret < 0)
+ rdev_err(rdev, "couldn't disable parent: %d\n",
+ ret);
+ }
}
mutex_unlock(®ulator_list_mutex);
return 0;
}
+
+#ifdef CONFIG_DEBUG_FS
+static int regulator_syncevent(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct regulator_dev *rdev;
+ char buffer[40];
+ int buf_size;
+
+ memset(buffer, 0, sizeof(buffer));
+ buf_size = min(count, (sizeof(buffer)-1));
+
+ if (copy_from_user(buffer, user_buf, buf_size))
+ return -EFAULT;
+
+ if (!strnicmp("all", buffer, 3)) {
+
+ mutex_lock(®ulator_list_mutex);
+
+ list_for_each_entry(rdev, ®ulator_list, list) {
+ mutex_lock(&rdev->mutex);
+
+ if (_regulator_is_enabled(rdev))
+ trace_regulator_enable(rdev_get_name(rdev));
+ else
+ trace_regulator_disable(rdev_get_name(rdev));
+
+ trace_regulator_set_voltage(rdev_get_name(rdev),
+ _regulator_get_voltage(rdev),
+ _regulator_get_voltage(rdev));
+
+ mutex_unlock(&rdev->mutex);
+ }
+ }
+
+ mutex_unlock(®ulator_list_mutex);
+
+ return count;
+}
+
+static const struct file_operations regulator_syncevent_fops = {
+ .write = regulator_syncevent,
+};
+
+static int __init regulator_init_debugfs(void)
+{
+ debugfs_create_file("syncevent_regulators", S_IWUSR, NULL, NULL,
+ ®ulator_syncevent_fops);
+
+ return 0;
+}
+
+late_initcall(regulator_init_debugfs);
+#endif
+
late_initcall(regulator_init_complete);