Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8f6862d4 authored by Mark Brown's avatar Mark Brown
Browse files
regulator: Bypass mode support

Allow regulators to be put into a non-regulating mode bypassing the
input straight to the output, mostly used by low power retention modes.
parents f76fe059 b8575a11
Loading
Loading
Loading
Loading
+21 −0
Original line number Diff line number Diff line
@@ -349,3 +349,24 @@ Description:

		This will be one of the same strings reported by
		the "state" attribute.

What:		/sys/class/regulator/.../bypass
Date:		September 2012
KernelVersion:	3.7
Contact:	Mark Brown <broonie@opensource.wolfsonmicro.com>
Description:
		Some regulator directories will contain a field called
		bypass.  This indicates if the device is in bypass mode.

		This will be one of the following strings:

		'enabled'
		'disabled'
		'unknown'

		'enabled' means the regulator is in bypass mode.

		'disabled' means that the regulator is regulating.

		'unknown' means software cannot determine the state, or
		the reported state is invalid.
+5 −0
Original line number Diff line number Diff line
@@ -434,6 +434,11 @@ static int __devinit arizona_extcon_probe(struct platform_device *pdev)
	regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE,
			   ARIZONA_JD1_ENA, ARIZONA_JD1_ENA);

	ret = regulator_allow_bypass(info->micvdd, true);
	if (ret != 0)
		dev_warn(arizona->dev, "Failed to set MICVDD to bypass: %d\n",
			 ret);

	pm_runtime_put(&pdev->dev);

	return 0;
+4 −0
Original line number Diff line number Diff line
@@ -39,6 +39,8 @@ static struct regulator_ops arizona_ldo1_ops = {
	.map_voltage = regulator_map_voltage_linear,
	.get_voltage_sel = regulator_get_voltage_sel_regmap,
	.set_voltage_sel = regulator_set_voltage_sel_regmap,
	.get_bypass = regulator_get_bypass_regmap,
	.set_bypass = regulator_set_bypass_regmap,
};

static const struct regulator_desc arizona_ldo1 = {
@@ -49,6 +51,8 @@ static const struct regulator_desc arizona_ldo1 = {

	.vsel_reg = ARIZONA_LDO1_CONTROL_1,
	.vsel_mask = ARIZONA_LDO1_VSEL_MASK,
	.bypass_reg = ARIZONA_LDO1_CONTROL_1,
	.bypass_mask = ARIZONA_LDO1_BYPASS,
	.min_uV = 900000,
	.uV_step = 50000,
	.n_voltages = 7,
+5 −0
Original line number Diff line number Diff line
@@ -82,6 +82,9 @@ static struct regulator_ops arizona_micsupp_ops = {

	.get_voltage_sel = regulator_get_voltage_sel_regmap,
	.set_voltage_sel = regulator_set_voltage_sel_regmap,

	.get_bypass = regulator_get_bypass_regmap,
	.set_bypass = regulator_set_bypass_regmap,
};

static const struct regulator_desc arizona_micsupp = {
@@ -95,6 +98,8 @@ static const struct regulator_desc arizona_micsupp = {
	.vsel_mask = ARIZONA_LDO2_VSEL_MASK,
	.enable_reg = ARIZONA_MIC_CHARGE_PUMP_1,
	.enable_mask = ARIZONA_CPMIC_ENA,
	.bypass_reg = ARIZONA_MIC_CHARGE_PUMP_1,
	.bypass_mask = ARIZONA_CPMIC_BYPASS,

	.owner = THIS_MODULE,
};
+126 −0
Original line number Diff line number Diff line
@@ -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,100 @@ int regulator_set_optimum_mode(struct regulator *regulator, int uA_load)
}
EXPORT_SYMBOL_GPL(regulator_set_optimum_mode);

/**
 * regulator_set_bypass_regmap - Default set_bypass() using regmap
 *
 * @rdev: device to operate on.
 * @enable: state to set.
 */
int regulator_set_bypass_regmap(struct regulator_dev *rdev, bool enable)
{
	unsigned int val;

	if (enable)
		val = rdev->desc->bypass_mask;
	else
		val = 0;

	return regmap_update_bits(rdev->regmap, rdev->desc->bypass_reg,
				  rdev->desc->bypass_mask, val);
}
EXPORT_SYMBOL_GPL(regulator_set_bypass_regmap);

/**
 * regulator_get_bypass_regmap - Default get_bypass() using regmap
 *
 * @rdev: device to operate on.
 * @enable: current state.
 */
int regulator_get_bypass_regmap(struct regulator_dev *rdev, bool *enable)
{
	unsigned int val;
	int ret;

	ret = regmap_read(rdev->regmap, rdev->desc->bypass_reg, &val);
	if (ret != 0)
		return ret;

	*enable = val & rdev->desc->bypass_mask;

	return 0;
}
EXPORT_SYMBOL_GPL(regulator_get_bypass_regmap);

/**
 * 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 +3155,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 +3248,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);
}

/**
Loading