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

Commit 72265a6d authored by Osvaldo Banuelos's avatar Osvaldo Banuelos
Browse files

regulator: cpr3-regulator: add support for LDO maximum headroom



In order to maintain optimum operability of the LDO hardware it is
necessary to impose a maximum voltage difference between the input
and output voltage of the LDOs. Add support for a device tree property
which specifies this maximum voltage difference and enforce it when
configuring the LDO hardware whenever a CPR corner change request
is handled.

Change-Id: Ic58cb16714109b5845d1ed5755a57c62078683b0
Signed-off-by: default avatarOsvaldo Banuelos <osvaldob@codeaurora.org>
parent bdac6adb
Loading
Loading
Loading
Loading
+22 −4
Original line number Diff line number Diff line
@@ -79,6 +79,12 @@ HMSS specific properties:
		    which may be used as the ceiling for the corner.  If this
		    property is not specified, then a value of 0 is assumed.

- qcom,system-supply-max-voltage
	Usage:      required if qcom,vdd-threadN-ldo-supply is specified for any
		    CPR3 regulator managed by any CPR3 thread of this controller.
	Value type: <u32>
	Definition: Maximum voltage setpoint for the system-supply regulator.

- qcom,cpr-up-down-delay-time
	Usage:      required
	Value type: <u32>
@@ -174,7 +180,7 @@ HMSS specific properties:
		    given regulator type.  For HMSS the maximum supported value
		    is 2.

- qcom,ldo-headroom-voltage
- qcom,ldo-min-headroom-voltage
	Usage:      required if qcom,vdd-threadN-ldo-supply is specified for the
		    CPR3 thread containing this CPR3 regulator and this CPR3
		    regulator needs to manage the cluster LDO state.
@@ -182,6 +188,15 @@ HMSS specific properties:
	Definition: Voltage in microvolts required between the VDD_APCC voltage
		    and the LDO output in order for the LDO to be operational.

- qcom,ldo-max-headroom-voltage
	Usage:      required if qcom,vdd-threadN-ldo-supply is specified for the
		    CPR3 thread containing this CPR3 regulator and this CPR3
		    regulator needs to manage the cluster LDO state.
	Value type: <u32>
	Definition: Maximum voltage difference in microvolts between the vdd-supply
		    voltage and the LDO output voltage in order for active LDO mode
		    to be operational.

- qcom,ldo-adjust-voltage
	Usage:      optional
	Value type: <u32>
@@ -189,7 +204,7 @@ HMSS specific properties:
		    output and CPU.

- qcom,ldo-max-voltage
	Usage:      required if qcom,ldo-headroom-voltage is specified for this
	Usage:      required if qcom,ldo-min-headroom-voltage is specified for this
		    CPR3 regulator.
	Value type: <u32>
	Definition: Voltage in microvolts which represents the maximum
@@ -248,6 +263,7 @@ apcc_cpr: cpr3-ctrl@99e8000 {
	qcom,apm-ctrl = <&apc_apm>;
	qcom,apm-threshold-voltage = <850000>;
	qcom,apm-hysteresis-voltage = <5000>;
	qcom,system-supply-max-voltage = <1015000>;

	vdd-supply = <&pm8994_s11>;
	qcom,voltage-step = <5000>;
@@ -280,7 +296,8 @@ apcc_cpr: cpr3-ctrl@99e8000 {
			qcom,cpr-fuse-combos = <1>;
			qcom,cpr-corners = <19>;

			qcom,ldo-headroom-voltage = <150000>;
			qcom,ldo-min-headroom-voltage = <150000>;
			qcom,ldo-max-headroom-voltage = <470000>;
			qcom,ldo-max-voltage = <805000>;

			qcom,cpr-corner-fmax-map = <1 2 6 11 19>;
@@ -408,7 +425,8 @@ apcc_cpr: cpr3-ctrl@99e8000 {
			qcom,cpr-fuse-combos = <1>;
			qcom,cpr-corners = <18>;

			qcom,ldo-headroom-voltage = <150000>;
			qcom,ldo-min-headroom-voltage = <150000>;
			qcom,ldo-max-headroom-voltage = <470000>;
			qcom,ldo-max-voltage = <805000>;

			qcom,cpr-corner-fmax-map = <1 3 5 11 18>;
+5 −2
Original line number Diff line number Diff line
@@ -574,6 +574,7 @@
		qcom,apm-ctrl = <&apc_apm>;
		qcom,apm-threshold-voltage = <850000>;
		qcom,apm-hysteresis-voltage = <5000>;
		qcom,system-supply-max-voltage = <1015000>;

		vdd-supply = <&pm8994_s11>;
		qcom,voltage-step = <5000>;
@@ -612,7 +613,8 @@
					/* Speed bin 1 */
					<13 13 13 13 13 13 13 13>;

				qcom,ldo-headroom-voltage = <150000>;
				qcom,ldo-min-headroom-voltage = <150000>;
				qcom,ldo-max-headroom-voltage = <470000>;
				qcom,ldo-max-voltage = <890000>;

				qcom,cpr-corner-fmax-map =
@@ -947,7 +949,8 @@
					/* Speed bin 1 */
					<21 21 21 21 21 21 21 21>;

				qcom,ldo-headroom-voltage = <150000>;
				qcom,ldo-min-headroom-voltage = <150000>;
				qcom,ldo-max-headroom-voltage = <470000>;
				qcom,ldo-max-voltage = <890000>;

				qcom,cpr-corner-fmax-map =
+0 −2
Original line number Diff line number Diff line
@@ -109,7 +109,6 @@
	qcom,cpr-fuse-combos = <4>;
	qcom,cpr-corners = <19>;

	qcom,ldo-headroom-voltage = <150000>;
	qcom,ldo-max-voltage = <805000>;

	qcom,cpr-corner-fmax-map = <1 2 6 11 19>;
@@ -227,7 +226,6 @@
	qcom,cpr-fuse-combos = <4>;
	qcom,cpr-corners = <18>;

	qcom,ldo-headroom-voltage = <150000>;
	qcom,ldo-max-voltage = <805000>;

	qcom,cpr-corner-fmax-map = <1 3 5 11 18>;
+26 −6
Original line number Diff line number Diff line
@@ -1279,7 +1279,7 @@ static int cpr3_hmss_kvreg_init(struct cpr3_regulator *vreg)

	if (!of_find_property(ctrl->dev->of_node, kvreg_name_buf , NULL))
		return 0;
	else if (!of_find_property(node, "qcom,ldo-headroom-voltage", NULL))
	else if (!of_find_property(node, "qcom,ldo-min-headroom-voltage", NULL))
		return 0;

	scnprintf(kvreg_name_buf, MAX_KVREG_NAME_SIZE, "vdd-thread%d-ldo", id);
@@ -1307,10 +1307,23 @@ static int cpr3_hmss_kvreg_init(struct cpr3_regulator *vreg)
		return rc;
	}

	rc = of_property_read_u32(node, "qcom,ldo-headroom-voltage",
				&vreg->ldo_headroom_volt);
	if (!ctrl->system_supply_max_volt) {
		cpr3_err(ctrl, "system-supply max voltage must be specified\n");
		return -EINVAL;
	}

	rc = of_property_read_u32(node, "qcom,ldo-min-headroom-voltage",
				&vreg->ldo_min_headroom_volt);
	if (rc) {
		cpr3_err(vreg, "error reading qcom,ldo-headroom-voltage, rc=%d\n",
		cpr3_err(vreg, "error reading qcom,ldo-min-headroom-voltage, rc=%d\n",
			rc);
		return rc;
	}

	rc = of_property_read_u32(node, "qcom,ldo-max-headroom-voltage",
				  &vreg->ldo_max_headroom_volt);
	if (rc) {
		cpr3_err(vreg, "error reading qcom,ldo-max-headroom-voltage, rc=%d\n",
			 rc);
		return rc;
	}
@@ -1343,8 +1356,9 @@ static int cpr3_hmss_kvreg_init(struct cpr3_regulator *vreg)
	vreg->ldo_mode_allowed = !of_property_read_bool(node,
							"qcom,ldo-disable");

	cpr3_info(vreg, "LDO headroom=%d uV, LDO adj=%d uV, LDO mode=%s, LDO retention=%d uV\n",
		  vreg->ldo_headroom_volt,
	cpr3_info(vreg, "LDO min headroom=%d uV, LDO max headroom=%d uV, LDO adj=%d uV, LDO mode=%s, LDO retention=%d uV\n",
		  vreg->ldo_min_headroom_volt,
		  vreg->ldo_max_headroom_volt,
		  vreg->ldo_adjust_volt,
		  vreg->ldo_mode_allowed ? "allowed" : "disallowed",
		  vreg->ldo_ret_volt);
@@ -1598,6 +1612,12 @@ static int cpr3_hmss_init_controller(struct cpr3_controller *ctrl)
		return rc;
	}


	/* No error check since this is an optional property. */
	of_property_read_u32(ctrl->dev->of_node,
			     "qcom,system-supply-max-voltage",
			     &ctrl->system_supply_max_volt);

	/* No error check since this is an optional property. */
	of_property_read_u32(ctrl->dev->of_node, "qcom,cpr-clock-throttling",
			&ctrl->proc_clock_throttle);
+133 −29
Original line number Diff line number Diff line
@@ -782,7 +782,7 @@ static int cpr3_regulator_config_ldo_retention(struct cpr3_regulator *vreg,

	}

	mode = floor_volt >= retention_volt + vreg->ldo_headroom_volt
	mode = floor_volt >= retention_volt + vreg->ldo_min_headroom_volt
		? LDO_MODE : BHS_MODE;

	rc = regulator_allow_bypass(ldo_ret_reg, mode);
@@ -811,7 +811,7 @@ static int cpr3_regulator_set_bhs_mode(struct cpr3_regulator *vreg,
	struct regulator *ldo_reg = vreg->ldo_regulator;
	int bhs_volt, rc;

	bhs_volt = vdd_volt - vreg->ldo_headroom_volt;
	bhs_volt = vdd_volt - vreg->ldo_min_headroom_volt;
	if (bhs_volt > vreg->ldo_max_volt) {
		cpr3_debug(vreg, "limited to LDO output of %d uV when switching to BHS mode\n",
			   vreg->ldo_max_volt);
@@ -839,13 +839,18 @@ static int cpr3_regulator_set_bhs_mode(struct cpr3_regulator *vreg,

/**
 * cpr3_regulator_config_vreg_ldo() - configure the voltage and bypass state for
 *		the LDO regulator associated with a single CPR3 regulator
 *		the LDO regulator associated with a single CPR3 regulator.
 *
 * @vreg:		Pointer to the CPR3 regulator
 * @vdd_floor_volt:	Last known aggregated floor voltage in microvolts for
 *			the VDD supply
 * @vdd_ceiling_volt:	Last known aggregated ceiling voltage in microvolts for
 *			the VDD supply
 * @vdd_volt:		Last known voltage in microvolts for the VDD supply
 * @last_volt:		Last known voltage in microvolts for the VDD supply
 * @apm_crossing:	Flag indicating if an APM reconfiguration is taking
 *			place as a result of a VDD voltage scale request and
 *			the VDD supply is at the crossover voltage
 * @new_volt:		New voltage in microvolts that VDD needs to end up at
 *
 * This function performs all relevant LDO or BHS configurations if an LDO
 * regulator is specified.
@@ -854,10 +859,53 @@ static int cpr3_regulator_set_bhs_mode(struct cpr3_regulator *vreg,
 */
static int cpr3_regulator_config_vreg_ldo(struct cpr3_regulator *vreg,
			  int vdd_floor_volt, int vdd_ceiling_volt,
			     int vdd_volt)
			  int last_volt, bool apm_crossing, int new_volt)
{
	struct cpr3_controller *ctrl = vreg->thread->ctrl;
	struct regulator *ldo_reg = vreg->ldo_regulator;
	int rc, ldo_volt, bhs_volt, max_volt;
	struct cpr3_corner *current_corner;
	enum msm_apm_supply apm_mode;
	int rc, ldo_volt, final_ldo_volt, bhs_volt, max_volt, safe_volt;

	if (apm_crossing) {
		if (!vreg->vreg_enabled || vreg->current_corner
		    == CPR3_REGULATOR_CORNER_INVALID)
			return 0;

		/*
		 * Guarantee LDO maximum headroom is not
		 * violated when the APM is switched to the
		 * system-supply source.
		 */
		current_corner = &vreg->corner[vreg->current_corner];
		if (vreg->ldo_regulator_bypass == LDO_MODE) {
			apm_mode = msm_apm_get_supply(ctrl->apm);
			if (apm_mode < 0) {
				cpr3_err(ctrl, "APM get supply failed, rc=%d\n",
					 apm_mode);
				return apm_mode;
			}

			if (apm_mode == ctrl->apm_high_supply &&
			    new_volt < ctrl->apm_threshold_volt) {
				safe_volt = max(current_corner->open_loop_volt
						- vreg->ldo_adjust_volt,
						ctrl->system_supply_max_volt
						- vreg->ldo_max_headroom_volt);
				max_volt = min(ctrl->system_supply_max_volt,
					       vreg->ldo_max_volt);

				rc = regulator_set_voltage(ldo_reg, safe_volt,
							   max_volt);
				if (rc) {
					cpr3_err(vreg, "regulator_set_voltage(ldo) == %d failed, rc=%d\n",
						 safe_volt, rc);
					return rc;
				}
			}
		}
		return 0;
	}

	rc = cpr3_regulator_config_ldo_retention(vreg, vdd_floor_volt);
	if (rc)
@@ -867,34 +915,55 @@ static int cpr3_regulator_config_vreg_ldo(struct cpr3_regulator *vreg,
	    == CPR3_REGULATOR_CORNER_INVALID)
		return 0;

	ldo_volt = vreg->corner[vreg->current_corner].open_loop_volt
	current_corner = &vreg->corner[vreg->current_corner];
	ldo_volt = current_corner->open_loop_volt
		- vreg->ldo_adjust_volt;

	bhs_volt = last_volt - vreg->ldo_min_headroom_volt;
	max_volt = min(vdd_ceiling_volt, vreg->ldo_max_volt);

	if (vdd_floor_volt >= ldo_volt + vreg->ldo_headroom_volt) {
	if (vdd_floor_volt >= ldo_volt + vreg->ldo_min_headroom_volt &&
	    ldo_volt >= ctrl->system_supply_max_volt -
	    vreg->ldo_max_headroom_volt &&
	    bhs_volt >= ctrl->system_supply_max_volt -
	    vreg->ldo_max_headroom_volt) {
		/* LDO minimum and maximum headrooms satisfied */
		apm_mode = msm_apm_get_supply(ctrl->apm);
		if (apm_mode < 0) {
			cpr3_err(ctrl, "APM get supply failed, rc=%d\n",
				 apm_mode);
			return apm_mode;
		}

		if (vreg->ldo_regulator_bypass == BHS_MODE) {
			if (ldo_volt > vreg->ldo_max_volt) {
				cpr3_debug(vreg, "cannot support LDO mode with ldo_volt=%d uV\n",
					   ldo_volt);
				return 0;
			}

			/*
			 * BHS to LDO transition. Configure LDO output
			 * to min(max LDO output, VDD - LDO headroom)
			 * voltage then switch the regulator mode.
			 */
			bhs_volt = vdd_volt - vreg->ldo_headroom_volt;
			if (bhs_volt > vreg->ldo_max_volt) {
				cpr3_debug(vreg, "limiting bhs_volt=%d uV to %d uV\n",
					   bhs_volt, vreg->ldo_max_volt);
				bhs_volt = vreg->ldo_max_volt;
			}
			 * voltage if APM is on high supply source or
			 * min(max(system-supply ceiling - LDO max headroom,
			 * VDD - LDO headroom), max LDO output) if
			 * APM is on low supply source, then switch
			 * regulator mode.
			 */
			if (apm_mode == ctrl->apm_high_supply)
				safe_volt = min(vreg->ldo_max_volt, bhs_volt);
			else
				safe_volt =
					min(max(ctrl->system_supply_max_volt -
						vreg->ldo_max_headroom_volt,
						bhs_volt),
					    vreg->ldo_max_volt);

			rc = regulator_set_voltage(ldo_reg, bhs_volt, max_volt);
			rc = regulator_set_voltage(ldo_reg, safe_volt,
						   max_volt);
			if (rc) {
				cpr3_err(vreg, "regulator_set_voltage(ldo) == %d failed, rc=%d\n",
					 bhs_volt, rc);
					 safe_volt, rc);
				return rc;
			}

@@ -908,15 +977,22 @@ static int cpr3_regulator_config_vreg_ldo(struct cpr3_regulator *vreg,
		}

		/* Configure final LDO output voltage */
		rc = regulator_set_voltage(ldo_reg, ldo_volt, max_volt);
		if (apm_mode == ctrl->apm_high_supply)
			final_ldo_volt = max(ldo_volt,
					     vdd_ceiling_volt -
					     vreg->ldo_max_headroom_volt);
		else
			final_ldo_volt = ldo_volt;

		rc = regulator_set_voltage(ldo_reg, final_ldo_volt, max_volt);
		if (rc) {
			cpr3_err(vreg, "regulator_set_voltage(ldo) == %d failed, rc=%d\n",
				 ldo_volt, rc);
				 final_ldo_volt, rc);
			return rc;
		}
	} else {
		if (vreg->ldo_regulator_bypass == LDO_MODE) {
			rc = cpr3_regulator_set_bhs_mode(vreg, vdd_volt,
			rc = cpr3_regulator_set_bhs_mode(vreg, last_volt,
							vdd_ceiling_volt);
			if (rc)
				return rc;
@@ -935,13 +1011,17 @@ static int cpr3_regulator_config_vreg_ldo(struct cpr3_regulator *vreg,
 *			the VDD supply
 * @vdd_ceiling_volt:	Last known aggregated ceiling voltage in microvolts for
 *			the VDD supply
 * @vdd_volt:		Last known voltage in microvolts for the VDD supply
 * @last_volt:		Last known voltage in microvolts for the VDD supply
 * @apm_crossing:	Flag indicating if an APM reconfiguration is taking
 *			place as a result of a VDD voltage scale request and
 *			the VDD supply is at the crossover voltage
 * @new_volt:		New voltage in microvolts that VDD needs to end up at
 *
 * Return: 0 on success, errno on failure
 */
static int cpr3_regulator_config_ldo(struct cpr3_controller *ctrl,
			     int vdd_floor_volt, int vdd_ceiling_volt,
			     int vdd_volt)
			     int last_volt, bool apm_crossing, int new_volt)
{
	struct cpr3_regulator *vreg;
	int i, j, rc;
@@ -954,7 +1034,8 @@ static int cpr3_regulator_config_ldo(struct cpr3_controller *ctrl,
				continue;

			rc = cpr3_regulator_config_vreg_ldo(vreg,
				vdd_floor_volt, vdd_ceiling_volt, vdd_volt);
					vdd_floor_volt, vdd_ceiling_volt,
					last_volt, apm_crossing, new_volt);
			if (rc)
				return rc;
		}
@@ -999,6 +1080,18 @@ static int cpr3_regulator_scale_vdd_voltage(struct cpr3_controller *ctrl,
		apm_crossing = true;

	if (apm_crossing) {
		if (new_volt < last_volt) {
			/* Decreasing VDD voltage */
			rc = cpr3_regulator_config_ldo(ctrl, apm_volt,
						       last_max_volt, last_volt,
						       false, apm_volt);
			if (rc) {
				cpr3_err(ctrl, "unable to configure LDO state, rc=%d\n",
					 rc);
				return rc;
			}
		}

		rc = regulator_set_voltage(vdd, apm_volt, apm_volt);
		if (rc) {
			cpr3_err(ctrl, "regulator_set_voltage(vdd) == %d failed, rc=%d\n",
@@ -1006,6 +1099,15 @@ static int cpr3_regulator_scale_vdd_voltage(struct cpr3_controller *ctrl,
			return rc;
		}

		rc = cpr3_regulator_config_ldo(ctrl, aggr_corner->floor_volt,
					       last_max_volt, apm_volt, true,
					       new_volt);
		if (rc) {
			cpr3_err(ctrl, "unable to configure LDO state, rc=%d\n",
				 rc);
			return rc;
		}

		rc = msm_apm_set_supply(ctrl->apm, new_volt >= apm_volt
				? ctrl->apm_high_supply : ctrl->apm_low_supply);
		if (rc) {
@@ -1020,7 +1122,8 @@ static int cpr3_regulator_scale_vdd_voltage(struct cpr3_controller *ctrl,
		/* Decreasing VDD voltage */
		rc = cpr3_regulator_config_ldo(ctrl, aggr_corner->floor_volt,
					       last_max_volt, apm_crossing ?
					       apm_volt : last_volt);
					       apm_volt : last_volt, false,
					       new_volt);
		if (rc) {
			cpr3_err(ctrl, "unable to configure LDO state, rc=%d\n",
				 rc);
@@ -1056,7 +1159,8 @@ static int cpr3_regulator_scale_vdd_voltage(struct cpr3_controller *ctrl,
	if (new_volt >= last_volt) {
		/* Increasing VDD voltage */
		rc = cpr3_regulator_config_ldo(ctrl, aggr_corner->floor_volt,
					       max_volt, new_volt);
					       max_volt, new_volt, false,
					       new_volt);
		if (rc) {
			cpr3_err(ctrl, "unable to configure LDO state, rc=%d\n",
				 rc);
Loading