regulator: add support for changing control mode of regulator
[linux-2.6.git] / drivers / regulator / core.c
index 5c17c50..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)
@@ -2498,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