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

Commit 624bba60 authored by David Collins's avatar David Collins
Browse files

regulator: cpr-regulator: add support for per-RO CPR step quotient



Add support for using a different CPR step quotient value for
each possible ring oscillator (RO).  This ensures that equivalent
CPR dynamic voltage margins can be achieved on a given chip
regardless of which RO is used.

Also clear all RBCPR_GCNT_TARGET registers before switching
voltage corners so that only the expected RO is used.  This is
needed in order to support different RO's for each fuse corner.

Change-Id: If28d30c0f979669f46936f4e064119a69834d1e1
Signed-off-by: default avatarDavid Collins <collinsd@codeaurora.org>
parent b071bab9
Loading
Loading
Loading
Loading
+5 −2
Original line number Diff line number Diff line
@@ -44,8 +44,11 @@ Required properties:
- qcom,cpr-timer-cons-down:	Consecutive number of timer interval (qcom,cpr-timer-delay)
				occurred before issuing DOWN interrupt.
- qcom,cpr-irq-line:		Internal interrupt route signal of RBCPR, one of 0, 1 or 2.
- qcom,cpr-step-quotient:	Number of CPR quotient (Ring Oscillator(RO) count) per vdd-apc-supply step
				to issue error_steps.
- qcom,cpr-step-quotient:	Defines the number of CPR quotient (i.e. Ring Oscillator(RO)
				count) per vdd-apc-supply output voltage step.  A single
				integer value may be specified which is to be used for all
				RO's.  Alternatively, 8 integer values may be specified which
				define the step quotients for RO0 to RO7 in order.
- qcom,cpr-up-threshold:	The threshold for CPR to issue interrupt when
				error_steps is greater than it when stepping up.
- qcom,cpr-down-threshold:	The threshold for CPR to issue interrupt when
+73 −11
Original line number Diff line number Diff line
@@ -274,7 +274,7 @@ struct cpr_regulator {
	u32		timer_cons_up;
	u32		timer_cons_down;
	u32		irq_line;
	u32		step_quotient;
	u32		*step_quotient;
	u32		up_threshold;
	u32		down_threshold;
	u32		idle_clocks;
@@ -572,13 +572,25 @@ static void cpr_corner_save(struct cpr_regulator *cpr_vreg, int corner)

static void cpr_corner_restore(struct cpr_regulator *cpr_vreg, int corner)
{
	u32 gcnt, ctl, irq, ro_sel;
	u32 gcnt, ctl, irq, ro_sel, step_quot;
	int fuse_corner = cpr_vreg->corner_map[corner];
	int i;

	ro_sel = cpr_vreg->cpr_fuse_ro_sel[fuse_corner];
	gcnt = cpr_vreg->gcnt | (cpr_vreg->cpr_fuse_target_quot[fuse_corner] -
					cpr_vreg->quot_adjust[corner]);

	/* Program the step quotient and idle clocks */
	step_quot = ((cpr_vreg->idle_clocks & RBCPR_STEP_QUOT_IDLE_CLK_MASK)
			<< RBCPR_STEP_QUOT_IDLE_CLK_SHIFT) |
		(cpr_vreg->step_quotient[fuse_corner]
			& RBCPR_STEP_QUOT_STEPQUOT_MASK);
	cpr_write(cpr_vreg, REG_RBCPR_STEP_QUOT, step_quot);

	/* Clear the target quotient value and gate count of all ROs */
	for (i = 0; i < CPR_NUM_RING_OSC; i++)
		cpr_write(cpr_vreg, REG_RBCPR_GCNT_TARGET(i), 0);

	cpr_write(cpr_vreg, REG_RBCPR_GCNT_TARGET(ro_sel), gcnt);
	ctl = cpr_vreg->save_ctl[corner];
	cpr_write(cpr_vreg, REG_RBCPR_CTL, ctl);
@@ -1150,12 +1162,6 @@ static int cpr_config(struct cpr_regulator *cpr_vreg, struct device *dev)
			RBCPR_GCNT_TARGET_GCNT_SHIFT;
	cpr_vreg->gcnt = gcnt;

	/* Program the step quotient and idle clocks */
	val = ((cpr_vreg->idle_clocks & RBCPR_STEP_QUOT_IDLE_CLK_MASK)
			<< RBCPR_STEP_QUOT_IDLE_CLK_SHIFT) |
		(cpr_vreg->step_quotient & RBCPR_STEP_QUOT_STEPQUOT_MASK);
	cpr_write(cpr_vreg, REG_RBCPR_STEP_QUOT, val);

	/* Program the delay count for the timer */
	val = (cpr_vreg->ref_clk_khz * cpr_vreg->timer_delay_us) / 1000;
	cpr_write(cpr_vreg, REG_RBCPR_TIMER_INTERVAL, val);
@@ -2919,6 +2925,59 @@ static int cpr_init_ceiling_floor_override_voltages(
	return rc;
}

static int cpr_init_step_quotient(struct platform_device *pdev,
		  struct cpr_regulator *cpr_vreg)
{
	struct device_node *of_node = pdev->dev.of_node;
	int len = 0;
	u32 step_quot[CPR_NUM_RING_OSC];
	int i, rc;

	if (!of_find_property(of_node, "qcom,cpr-step-quotient", &len)) {
		cpr_err(cpr_vreg, "qcom,cpr-step-quotient property missing\n");
		return -EINVAL;
	}

	if (len == sizeof(u32)) {
		/* Single step quotient used for all ring oscillators. */
		rc = of_property_read_u32(of_node, "qcom,cpr-step-quotient",
					step_quot);
		if (rc) {
			cpr_err(cpr_vreg, "could not read qcom,cpr-step-quotient, rc=%d\n",
				rc);
			return rc;
		}

		for (i = CPR_FUSE_CORNER_MIN; i <= cpr_vreg->num_fuse_corners;
		     i++)
			cpr_vreg->step_quotient[i] = step_quot[0];
	} else if (len == sizeof(u32) * CPR_NUM_RING_OSC) {
		/* Unique step quotient used per ring oscillator. */
		rc = of_property_read_u32_array(of_node,
			"qcom,cpr-step-quotient", step_quot, CPR_NUM_RING_OSC);
		if (rc) {
			cpr_err(cpr_vreg, "could not read qcom,cpr-step-quotient, rc=%d\n",
				rc);
			return rc;
		}

		for (i = CPR_FUSE_CORNER_MIN; i <= cpr_vreg->num_fuse_corners;
		     i++)
			cpr_vreg->step_quotient[i]
				= step_quot[cpr_vreg->cpr_fuse_ro_sel[i]];
	} else {
		cpr_err(cpr_vreg, "qcom,cpr-step-quotient has invalid length=%d\n",
			len);
		return -EINVAL;
	}

	for (i = CPR_FUSE_CORNER_MIN; i <= cpr_vreg->num_fuse_corners; i++)
		cpr_debug(cpr_vreg, "step_quotient[%d]=%u\n", i,
			cpr_vreg->step_quotient[i]);

	return 0;
}

static int cpr_init_cpr_parameters(struct platform_device *pdev,
					  struct cpr_regulator *cpr_vreg)
{
@@ -2945,8 +3004,8 @@ static int cpr_init_cpr_parameters(struct platform_device *pdev,
			  &cpr_vreg->irq_line, rc);
	if (rc)
		return rc;
	CPR_PROP_READ_U32(cpr_vreg, of_node, "cpr-step-quotient",
			  &cpr_vreg->step_quotient, rc);

	rc = cpr_init_step_quotient(pdev, cpr_vreg);
	if (rc)
		return rc;

@@ -3333,12 +3392,15 @@ static int cpr_fuse_corner_array_alloc(struct device *dev,
		len * (sizeof(*cpr_vreg->fuse_ceiling_volt)), GFP_KERNEL);
	cpr_vreg->fuse_floor_volt = devm_kzalloc(dev,
		len * (sizeof(*cpr_vreg->fuse_floor_volt)), GFP_KERNEL);
	cpr_vreg->step_quotient = devm_kzalloc(dev,
		len * sizeof(*cpr_vreg->step_quotient), GFP_KERNEL);

	if (cpr_vreg->pvs_corner_v == NULL || cpr_vreg->cpr_fuse_ro_sel == NULL
	    || cpr_vreg->fuse_ceiling_volt == NULL
	    || cpr_vreg->fuse_floor_volt == NULL
	    || cpr_vreg->vdd_mx_corner_map == NULL
	    || cpr_vreg->cpr_fuse_target_quot == NULL) {
	    || cpr_vreg->cpr_fuse_target_quot == NULL
	    || cpr_vreg->step_quotient == NULL) {
		cpr_err(cpr_vreg, "Could not allocate memory for CPR arrays\n");
		return -ENOMEM;
	}