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

Commit 48f93b07 authored by Subbaraman Narayanamurthy's avatar Subbaraman Narayanamurthy
Browse files

regulator: cpr-regulator: Add support for fuse based quotient offset



In some SOCs, quotient offsets per fuse corner can be programmed
in the fuse so that the software can make use of them without the
need to calculate them. Add support to read those quotient offsets
which are defined per fuse corner and use them to obtain the
quotient scaling factors, so that the quotient for a particular
frequency can be calculated.

In addition, add support to adjust target quotient offset based
on the values read from device tree.

Change-Id: I63c3376ea43eaff276bdeb0fdd7000c024abd46e
Signed-off-by: default avatarSubbaraman Narayanamurthy <subbaram@codeaurora.org>
parent dca34e2b
Loading
Loading
Loading
Loading
+47 −0
Original line number Diff line number Diff line
@@ -294,6 +294,22 @@ Optional properties:
				applied unconditionally.  This property can be used to add or subtract
				static initial voltage margin from the regulator managed by the CPR
				controller.
- qcom,cpr-quot-offset-adjustment:	Array of integer tuples of target quotient offset adjustments to add
				to the fused quotient offsets of each fuse corner. The elements in a tuple
				are ordered from lowest voltage corner to highest voltage corner. Each tuple
				must be of length defined by qcom,cpr-fuse-corners. If the qcom,cpr-fuse-version-map
				property is specified, then qcom,cpr-quot-offset-adjustment must contain the
				same number of tuples as qcom,cpr-fuse-version-map.  These tuples are then
				mapped one-to-one in the order specified.  E.g. if the second
				qcom,cpr-fuse-version-map tuple matches for a given device, then the quotient
				offset adjustments defined in the second qcom,cpr-quot-offset-adjustment tuple
				will be applied. If the qcom,cpr-fuse-version-map property is not specified,
				then qcom,cpr-quot-offset-adjustment must contain a single tuple which is then
				applied unconditionally.  If this property is specified, then the quotient
				offset adjustment values are added to the target quotient offset values read
				from fuses.
				This property can be used to add or subtract static quotient offset margin from
				the regulator managed by the CPR controller.
- qcom,cpr-clamp-timer-interval:	The number of 64 reference clock cycle blocks to delay for whenever
					the clamp signal, sensor mask registers or sensor bypass registers
					change.  The CPR controller loop is disabled during this delay.
@@ -514,6 +530,25 @@ Optional properties:
- qcom,cpr-quot-min-diff:	Integer which defines the minimum target-quotient difference between
				the highest and (highest - 1) fuse corner to keep CPR enabled. If this
				property is not specified a default value of 50 is used.
- qcom,cpr-fuse-quot-offset:	Array of quadruples in which each quadruple specifies a fuse location to
				read in order to get the quotient offset for a fuse corner. The fuse values
				are encoded as the difference between quotients of that fuse corner and its
				adjacent lower fuse corner divided by an unpacking multiplier.
				At present, the multiplier is assumed to be same as that of the value defined
				under qcom,cpr-fuse-target-quot-scale.
				The 4 elements in one quadruple are:
				[0]: => the fuse row number of the bits
				[1]: => LSB bit position of the bits
				[2]: => number of the bits
				[3]: => fuse reading method, 0 for direct reading or 1 for SCM reading
				The quadruples are ordered from the lowest fuse corner to the highest
				fuse corner.
				Quotient offset read from the fuse locations above can be overridden with
				the property qcom,cpr-quot-adjust-scaling-factor-max.
- qcom,cpr-redun-fuse-quot-offset: Array of quadruples in which each quadruple specifies a fuse location to
				read in order to get the redundant quotient offset for a fuse corner. This
				property is the same as qcom,cpr-fuse-quot-offset except that it is only
				utilized if a chip is configured to use the redundant set of fuse values.

Example:
	apc_vreg_corner: regulator@f9018000 {
@@ -631,6 +666,16 @@ Example:
				<0 1>,
				<0 1>;
		qcom,cpr-quot-adjust-scaling-factor-max = <0 650 650>;
		qcom,cpr-fuse-quot-offset =
				<138 53 5 0>,
				<138 53 5 0>,
				<138 48 5 0>,
				<138 58 5 0>;
		qcom,cpr-fuse-redun-quot-offset =
				<200 53 5 0>,
				<200 53 5 0>,
				<200 48 5 0>,
				<200 58 5 0>;
		qcom,cpr-fuse-init-voltage =
				<27 36 6 0>,
				<27 18 6 0>,
@@ -659,6 +704,8 @@ Example:
				<0 0 (-210)>,
				<0 0 (-60)>,
				<0 0 (-94)>;
		qcom,cpr-quot-offset-adjustment =
				<0 0 (-5)>;
		qcom,cpr-init-voltage-adjustment =
				<0 0 (-100000)>,
				<0 0 (-100000)>,
+144 −9
Original line number Diff line number Diff line
@@ -192,6 +192,11 @@ struct quot_adjust_info {
	int quot_adjust;
};

struct cpr_quot_scale {
	u32 offset;
	u32 multiplier;
};

static const char * const vdd_apc_name[] =	{"vdd-apc-optional-prim",
						"vdd-apc-optional-sec",
						"vdd-apc"};
@@ -249,6 +254,7 @@ struct cpr_regulator {
	int		cpr_fuse_map_match;
	int		*cpr_fuse_target_quot;
	int		*cpr_fuse_ro_sel;
	int		*fuse_quot_offset;
	int		gcnt;

	unsigned int	cpr_irq;
@@ -1935,6 +1941,127 @@ static int cpr_reduce_ceiling_voltage(struct cpr_regulator *cpr_vreg,
	return 0;
}

static int cpr_adjust_target_quot_offsets(struct platform_device *pdev,
					struct cpr_regulator *cpr_vreg)
{
	struct device_node *of_node = pdev->dev.of_node;
	int tuple_count, tuple_match, i;
	u32 index;
	u32 quot_offset_adjust = 0;
	int len = 0;
	int rc = 0;
	char *quot_offset_str;

	quot_offset_str = "qcom,cpr-quot-offset-adjustment";
	if (!of_find_property(of_node, quot_offset_str, &len)) {
		/* No static quotient adjustment needed. */
		return 0;
	}

	if (cpr_vreg->cpr_fuse_map_count) {
		if (cpr_vreg->cpr_fuse_map_match == FUSE_MAP_NO_MATCH) {
			/* No matching index to use for quotient adjustment. */
			return 0;
		}
		tuple_count = cpr_vreg->cpr_fuse_map_count;
		tuple_match = cpr_vreg->cpr_fuse_map_match;
	} else {
		tuple_count = 1;
		tuple_match = 0;
	}

	if (len != cpr_vreg->num_fuse_corners * tuple_count * sizeof(u32)) {
		cpr_err(cpr_vreg, "%s length=%d is invalid\n", quot_offset_str,
			len);
		return -EINVAL;
	}

	for (i = CPR_FUSE_CORNER_MIN; i <= cpr_vreg->num_fuse_corners; i++) {
		index = tuple_match * cpr_vreg->num_fuse_corners
				+ i - CPR_FUSE_CORNER_MIN;
		rc = of_property_read_u32_index(of_node, quot_offset_str, index,
			&quot_offset_adjust);
		if (rc) {
			cpr_err(cpr_vreg, "could not read %s index %u, rc=%d\n",
				quot_offset_str, index, rc);
			return rc;
		}

		if (quot_offset_adjust) {
			cpr_vreg->fuse_quot_offset[i] += quot_offset_adjust;
			cpr_info(cpr_vreg, "Corner[%d]: adjusted target quot = %d\n",
				i, cpr_vreg->fuse_quot_offset[i]);
		}
	}

	return rc;
}

static int cpr_get_fuse_quot_offset(struct cpr_regulator *cpr_vreg,
					struct platform_device *pdev,
					struct cpr_quot_scale *quot_scale)
{
	struct device *dev = &pdev->dev;
	struct property *prop;
	u32 *fuse_sel, *tmp;
	int rc = 0, i, size;
	char *quot_offset_str;

	quot_offset_str = cpr_vreg->cpr_fuse_redundant
			? "qcom,cpr-fuse-redun-quot-offset"
			: "qcom,cpr-fuse-quot-offset";

	prop = of_find_property(dev->of_node, quot_offset_str, NULL);
	if (!prop) {
		cpr_debug(cpr_vreg, "%s not present\n", quot_offset_str);
		return 0;
	} else {
		size = prop->length / sizeof(u32);
		if (size != cpr_vreg->num_fuse_corners * 4) {
			cpr_err(cpr_vreg, "fuse position for quot offset is invalid\n");
			return -EINVAL;
		}
	}

	fuse_sel = kzalloc(sizeof(u32) * size, GFP_KERNEL);
	if (!fuse_sel) {
		cpr_err(cpr_vreg, "memory alloc failed.\n");
		return -ENOMEM;
	}

	rc = of_property_read_u32_array(dev->of_node, quot_offset_str,
			fuse_sel, size);

	if (rc < 0) {
		cpr_err(cpr_vreg, "read %s failed, rc = %d\n", quot_offset_str,
			rc);
		kfree(fuse_sel);
		return rc;
	}

	cpr_vreg->fuse_quot_offset = devm_kzalloc(dev,
			sizeof(u32) * (cpr_vreg->num_corners + 1),
			GFP_KERNEL);
	if (!cpr_vreg->fuse_quot_offset) {
		cpr_err(cpr_vreg, "Can't allocate memory for cpr_vreg->fuse_quot_offset\n");
		kfree(fuse_sel);
		return -ENOMEM;
	}

	tmp = fuse_sel;
	for (i = CPR_FUSE_CORNER_MIN; i <= cpr_vreg->num_fuse_corners; i++) {
		cpr_vreg->fuse_quot_offset[i] = cpr_read_efuse_param(cpr_vreg,
					fuse_sel[0], fuse_sel[1], fuse_sel[2],
					fuse_sel[3]);
		cpr_vreg->fuse_quot_offset[i] *= quot_scale[i].multiplier;
		fuse_sel += 4;
	}

	rc = cpr_adjust_target_quot_offsets(pdev, cpr_vreg);
	kfree(tmp);
	return rc;
}

/*
 * Adjust the per-virtual-corner open loop voltage with an offset specfied by a
 * device-tree property. This must be called after open-loop voltage scaling.
@@ -2335,6 +2462,10 @@ static int cpr_get_corner_quot_adjustment(struct cpr_regulator *cpr_vreg,
		freq_max[i] /= 1000000;

	for (i = CPR_FUSE_CORNER_MIN + 1; i <= highest_fuse_corner; i++) {
		if (cpr_vreg->fuse_quot_offset)
			scaling[i] = 1000 * cpr_vreg->fuse_quot_offset[i]
				/ (freq_max[i] - freq_max[i - 1]);
		else
			scaling[i] = 1000 * (cpr_vreg->cpr_fuse_target_quot[i]
				      - cpr_vreg->cpr_fuse_target_quot[i - 1])
				  / (freq_max[i] - freq_max[i - 1]);
@@ -2402,11 +2533,6 @@ free_arrays_1:
	return rc;
}

struct cpr_quot_scale {
	u32 offset;
	u32 multiplier;
};

/*
 * Check if the redundant set of CPR fuses should be used in place of the
 * primary set and configure the cpr_fuse_redundant element accordingly.
@@ -2866,6 +2992,15 @@ static int cpr_init_cpr_efuse(struct platform_device *pdev,
		}
	}

	/*
	 * Check whether the fuse-quot-offset is defined per fuse corner.
	 * If it is defined, use it (quot_offset) in the calculation
	 * below for obtaining scaling factor per fuse corner.
	 */
	rc = cpr_get_fuse_quot_offset(cpr_vreg, pdev, quot_scale);
	if (rc < 0)
		goto error;

	rc = cpr_get_corner_quot_adjustment(cpr_vreg, &pdev->dev);
	if (rc)
		goto error;
@@ -2875,7 +3010,7 @@ static int cpr_init_cpr_efuse(struct platform_device *pdev,
		cpr_vreg->cpr_fuse_disable = true;
		cpr_err(cpr_vreg,
			"cpr_fuse_bits == 0; permanently disabling CPR\n");
	} else {
	} else if (!cpr_vreg->fuse_quot_offset) {
		/*
		 * Check if the target quotients for the highest two fuse
		 * corners are too close together.