|
@@ -77,6 +77,7 @@ struct regulator {
|
|
|
struct device *dev;
|
|
|
struct list_head list;
|
|
|
unsigned int always_on:1;
|
|
|
+ unsigned int bypass:1;
|
|
|
int uA_load;
|
|
|
int min_uV;
|
|
|
int max_uV;
|
|
@@ -394,6 +395,9 @@ static ssize_t regulator_status_show(struct device *dev,
|
|
|
case REGULATOR_STATUS_STANDBY:
|
|
|
label = "standby";
|
|
|
break;
|
|
|
+ case REGULATOR_STATUS_BYPASS:
|
|
|
+ label = "bypass";
|
|
|
+ break;
|
|
|
case REGULATOR_STATUS_UNDEFINED:
|
|
|
label = "undefined";
|
|
|
break;
|
|
@@ -585,6 +589,27 @@ 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_bypass_show(struct device *dev,
|
|
|
+ struct device_attribute *attr, char *buf)
|
|
|
+{
|
|
|
+ struct regulator_dev *rdev = dev_get_drvdata(dev);
|
|
|
+ const char *report;
|
|
|
+ bool bypass;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = rdev->desc->ops->get_bypass(rdev, &bypass);
|
|
|
+
|
|
|
+ if (ret != 0)
|
|
|
+ report = "unknown";
|
|
|
+ else if (bypass)
|
|
|
+ report = "enabled";
|
|
|
+ else
|
|
|
+ report = "disabled";
|
|
|
+
|
|
|
+ return sprintf(buf, "%s\n", report);
|
|
|
+}
|
|
|
+static DEVICE_ATTR(bypass, 0444,
|
|
|
+ regulator_bypass_show, NULL);
|
|
|
|
|
|
/*
|
|
|
* These are the only attributes are present for all regulators.
|
|
@@ -2673,6 +2698,59 @@ out:
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(regulator_set_optimum_mode);
|
|
|
|
|
|
+/**
|
|
|
+ * regulator_allow_bypass - allow the regulator to go into bypass mode
|
|
|
+ *
|
|
|
+ * @regulator: Regulator to configure
|
|
|
+ * @allow: 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
|
|
|
+ * constraints allow this. Bypass mode means that the regulator is
|
|
|
+ * simply passing the input directly to the output with no regulation.
|
|
|
+ */
|
|
|
+int regulator_allow_bypass(struct regulator *regulator, bool enable)
|
|
|
+{
|
|
|
+ struct regulator_dev *rdev = regulator->rdev;
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ if (!rdev->desc->ops->set_bypass)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ if (rdev->constraints &&
|
|
|
+ !(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_BYPASS))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ mutex_lock(&rdev->mutex);
|
|
|
+
|
|
|
+ if (enable && !regulator->bypass) {
|
|
|
+ rdev->bypass_count++;
|
|
|
+
|
|
|
+ if (rdev->bypass_count == rdev->open_count) {
|
|
|
+ ret = rdev->desc->ops->set_bypass(rdev, enable);
|
|
|
+ if (ret != 0)
|
|
|
+ rdev->bypass_count--;
|
|
|
+ }
|
|
|
+
|
|
|
+ } else if (!enable && regulator->bypass) {
|
|
|
+ rdev->bypass_count--;
|
|
|
+
|
|
|
+ if (rdev->bypass_count != rdev->open_count) {
|
|
|
+ ret = rdev->desc->ops->set_bypass(rdev, enable);
|
|
|
+ if (ret != 0)
|
|
|
+ rdev->bypass_count++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ret == 0)
|
|
|
+ regulator->bypass = enable;
|
|
|
+
|
|
|
+ mutex_unlock(&rdev->mutex);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(regulator_allow_bypass);
|
|
|
+
|
|
|
/**
|
|
|
* regulator_register_notifier - register regulator event notifier
|
|
|
* @regulator: regulator source
|
|
@@ -3036,6 +3114,11 @@ static int add_regulator_attributes(struct regulator_dev *rdev)
|
|
|
if (status < 0)
|
|
|
return status;
|
|
|
}
|
|
|
+ if (ops->get_bypass) {
|
|
|
+ status = device_create_file(dev, &dev_attr_bypass);
|
|
|
+ if (status < 0)
|
|
|
+ return status;
|
|
|
+ }
|
|
|
|
|
|
/* some attributes are type-specific */
|
|
|
if (rdev->desc->type == REGULATOR_CURRENT) {
|
|
@@ -3124,6 +3207,8 @@ static void rdev_init_debugfs(struct regulator_dev *rdev)
|
|
|
&rdev->use_count);
|
|
|
debugfs_create_u32("open_count", 0444, rdev->debugfs,
|
|
|
&rdev->open_count);
|
|
|
+ debugfs_create_u32("bypass_count", 0444, rdev->debugfs,
|
|
|
+ &rdev->bypass_count);
|
|
|
}
|
|
|
|
|
|
/**
|