regulator: add support for changing control mode of regulator
[linux-2.6.git] / drivers / regulator / core.c
index 48bd838..de6be43 100644 (file)
@@ -295,6 +295,20 @@ static int regulator_check_drms(struct regulator_dev *rdev)
        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 device_requested_uA_show(struct device *dev,
                             struct device_attribute *attr, char *buf)
 {
@@ -363,7 +377,7 @@ static ssize_t regulator_uV_show(struct device *dev,
 
        return ret;
 }
-static DEVICE_ATTR(microvolts, 0666, regulator_uV_show, regulator_uV_set);
+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)
@@ -1963,11 +1977,11 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev,
        min_uV += rdev->constraints->uV_offset;
        max_uV += rdev->constraints->uV_offset;
 
-       if (_regulator_is_enabled(rdev))
-               _notifier_call_chain(rdev, REGULATOR_EVENT_OUT_PRECHANGE,
-                                    NULL);
-
        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);
 
@@ -2017,6 +2031,10 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev,
                }
 
                if (best_val != INT_MAX) {
+                       if (_regulator_is_enabled(rdev))
+                               _notifier_call_chain(rdev,
+                               REGULATOR_EVENT_OUT_PRECHANGE, (void *)best_val);
+
                        ret = rdev->desc->ops->set_voltage_sel(rdev, selector);
                        selector = best_val;
                } else {
@@ -2038,9 +2056,12 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev,
                _notifier_call_chain(rdev, REGULATOR_EVENT_VOLTAGE_CHANGE,
                                     NULL);
 
-       if (_regulator_is_enabled(rdev))
+       if (_regulator_is_enabled(rdev)) {
+               if (selector != -1)
+                       min_uV = selector;
                _notifier_call_chain(rdev, REGULATOR_EVENT_OUT_POSTCHANGE,
-                                    NULL);
+                                    (void *)min_uV);
+       }
 
        trace_regulator_set_voltage_complete(rdev_get_name(rdev), selector);
 
@@ -2491,6 +2512,75 @@ out:
 EXPORT_SYMBOL_GPL(regulator_set_optimum_mode);
 
 /**
+ * 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_register_notifier - register regulator event notifier
  * @regulator: regulator source
  * @nb: notifier block
@@ -2527,7 +2617,7 @@ static void _notifier_call_chain(struct regulator_dev *rdev,
                                  unsigned long event, void *data)
 {
        /* call rdev chain first */
-       blocking_notifier_call_chain(&rdev->notifier, event, NULL);
+       blocking_notifier_call_chain(&rdev->notifier, event, data);
 }
 
 /**
@@ -3038,8 +3128,10 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc,
 
        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;