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

Commit 3659eb76 authored by Wu Fenglin's avatar Wu Fenglin Committed by David Collins
Browse files

msm: cpr-regulator: calculate quotient adjustment dynamically



Replace the fixed quotient adjustment algorithm with a new algorithm
that calculates quotient adjustments dynamically.

Calculate the quotient scaling factor according to fused target
quotients and application processor clock frequencies. A different
quotient adjustment value is applied to each voltage corner.

The formula for calculating the quotient adjustment value for each
voltage corner is:
quot_adjust = (freq_turbo_MHZ - freq_corner_MHZ) * scaling / 1000
where the "scaling" is determined using this equation:
scaling = min(max_scaling_factor,
1000 * (QUOT(fused_turbo) - QUOT(fused_norm)) /
(Freq_turbo_MHZ - Freq_normal_MHZ)).

In msm8x26 cpr-regulator device tree node, add a corner to frequency
mapping table, a speedbin + PVS version to max SVS/normal/turbo fuse
corner mapping table, a maximum quotient adjustment scaling factor,and
PVS version eFuse information. With the frequency to corner mapping
table specified in msm8x26 clock-a7 device tree node, this ensures that
1.6G/1.8G msm8226v2 and msm8926 parts receive power saving when running
at turbo frequencies below the maximum possible turbo frequency.

CRs-Fixed: 597721
Change-Id: Ideecccc2f81762bed3668cfdf27d942c00df76ea
Signed-off-by: default avatarWu Fenglin <fenglinw@codeaurora.org>
parent 8e5d905c
Loading
Loading
Loading
Loading
+56 −19
Original line number Original line Diff line number Diff line
@@ -203,21 +203,6 @@ Optional properties:
- qcom,cpr-uplift-speed-bin:	The speed bin value corresponding to one type of processor which needs to apply the
- qcom,cpr-uplift-speed-bin:	The speed bin value corresponding to one type of processor which needs to apply the
				pvs voltage uplift workaround.
				pvs voltage uplift workaround.
				This is required if cpr-fuse-uplift-disable-sel is present.
				This is required if cpr-fuse-uplift-disable-sel is present.
- qcom,cpr-quot-adjust-table:	Array of triples in which each triple indicates the speed bin of the CPU, the virtual
				corner to use and the quotient adjustment.
				The 3 elements in one triple are:
				[0]: => the speed bin of the CPU.
				[1]: => the virtual voltage corner to use.
				[2]: => the quotient adjustment for the corresponding virtual corner.
				If the speed bin in a triple is equal to the speed bin of the CPU, the adjustment would
				be subtracted from the quotient value of the voltage corner when the CPU is running at
				that virtual corner. Each virtual corner value must be in the range 1 to the number of
				elements in qcom,cpr-corner-map.
- qcom,cpr-corner-map:		Array of elements of fuse corner value for each virtual corner.
				The location or 1-based index of an element in the list corresponds to
				the virtual corner value. For example, the first element in the list is the fuse corner
				value that virtual corner 1 maps to.
				This is required if qcom,cpr-quot-adjust-table is present.
- qcom,cpr-quotient-adjustment:	Array of three elements of CPR quotient adjustments for each corner.
- qcom,cpr-quotient-adjustment:	Array of three elements of CPR quotient adjustments for each corner.
				The 3 quotient adjustments with index[0..2] are:
				The 3 quotient adjustments with index[0..2] are:
				[0] => amount to add to the SVS quotient
				[0] => amount to add to the SVS quotient
@@ -230,6 +215,42 @@ Optional properties:
				Not Present: No such regulator.
				Not Present: No such regulator.
- vdd-apc-optional-sec-supply:	Present: Regulator of second highest priority to supply VDD APC power.
- vdd-apc-optional-sec-supply:	Present: Regulator of second highest priority to supply VDD APC power.
				Not Present: No such regulator.
				Not Present: No such regulator.
- qcom,cpr-speed-bin-max-corners: Array of quintuples in which each quintuple maps a CPU speed bin and PVS version to
				the maximum virtual voltage corner corresponding to the SVS, NORMAL and TURBO corners.
				The 5 elements in one quintuple are:
				[0]: => the speed bin of the CPU.
				[1]: => the PVS version of the CPU.
				[2]: => the max virtual voltage corner value corresponding to SVS corner for this speed bin.
				[3]: => the max virtual voltage corner value corresponding to NORMAL corner for this speed bin.
				[4]: => the max virtual voltage corner value corresponding to TURBO corner for this speed bin.
				No CPR target quotient scaling is applied on chips which have a speed bin + PVS version
				pair that does not appear in one of the quintuples in this property. If the property is
				specified, then quotient scaling is enabled for the TURBO corner. If this property is
				not specified, then no quotient scaling can take place.
- qcom,cpr-corner-map:		Array of elements of fuse corner value for each virtual corner.
				The location or 1-based index of an element in the list corresponds to
				the virtual corner value. For example, the first element in the list is the fuse corner
				value that virtual corner 1 maps to.
				This property is required if qcom,cpr-speed-bin-max-corners is present.
- qcom,cpr-corner-frequency-map: Array of tuples in which a tuple describes a corner to application processor frequency
				mapping.
				The 2 elements in one tuple are:
				[0]: => a virtual voltage corner.
				[1]: => the application processor frequency in Hz corresponding to the virtual corner.
				This property is required if qcom,cpr-speed-bin-max-corners is present.
- qcom,pvs-version-fuse-sel:	Array of 4 elements to indicate where to read the pvs version of the processor,
				and the fuse reading method.
				The 4 elements with index[0..3] are:
				[0]: => the fuse row number of the selector;
				[1]: => LSB bit position of the bits;
				[2]: => the number of bits;
				[3]: => fuse reading method, 0 for direct reading or 1 for SCM reading.
				This property is required if qcom,cpr-speed-bin-max-corners is present.
- qcom,cpr-quot-adjust-scaling-factor-max: The maximum allowed CPR target quotient scaling factor to use when
				calculating the quotient adjustment for a given virtual voltage corner. It
				corresponds to 'scaling' in this equation:
				quot_adjust = (freq_turbo - freq_corner) * scaling / 1000.
				This property is required if qcom,cpr-speed-bin-max-corners is present.


Example:
Example:
	apc_vreg_corner: regulator@f9018000 {
	apc_vreg_corner: regulator@f9018000 {
@@ -319,9 +340,25 @@ Example:
		qcom,cpr-uplift-speed-bin = <1>;
		qcom,cpr-uplift-speed-bin = <1>;
		qcom,speed-bin-fuse-sel = <22 0 3 0>;
		qcom,speed-bin-fuse-sel = <22 0 3 0>;
		qcom,cpr-corner-map = <1 1 2 2 3 3 3 3 3 3 3 3>;
		qcom,cpr-corner-map = <1 1 2 2 3 3 3 3 3 3 3 3>;
		qcom,cpr-quot-adjust-table = <1 1 0>, <1 2 0>, <1 3 0>,
		qcom,cpr-corner-frequency-map =
						<1 4 0>, <1 5 450>, <1 6 375>,
				<1 300000000>,
						<1 7 300>, <1 8 225>, <1 9 187>,
				<2 384000000>,
						<1 10 150>, <1 11 75>, <1 12 0>;
				<3 600000000>,
				<4 787200000>,
				<5 998400000>,
				<6 1094400000>,
				<7 1190400000>,
				<8 1305600000>,
				<9 1344000000>,
				<10 1401600000>,
				<11 1497600000>,
				<12 1593600000>;
		qcom,pvs-version-fuse-sel = <22 4 2 0>;
		qcom,cpr-speed-bin-max-corners =
				<0 1 2 4 7>,
				<1 1 2 4 12>,
				<2 1 2 4 10>,
				<5 1 2 4 14>;
		qcom,cpr-quot-adjust-scaling-factor-max = <650>;
	};
	};
+22 −8
Original line number Original line Diff line number Diff line
@@ -37,14 +37,28 @@
	qcom,cpr-up-threshold = <0>;
	qcom,cpr-up-threshold = <0>;
	qcom,cpr-down-threshold = <5>;
	qcom,cpr-down-threshold = <5>;
	qcom,cpr-corner-map = <1 1 2 2 3 3 3 3 3 3 3 3 3 3>;
	qcom,cpr-corner-map = <1 1 2 2 3 3 3 3 3 3 3 3 3 3>;
	qcom,cpr-quot-adjust-table =
	qcom,pvs-version-fuse-sel = <22 4 2 0>;
				<1 5 450>,
	qcom,cpr-corner-frequency-map =
				<1 6 375>,
			<1 300000000>,
				<1 7 300>,
			<2 384000000>,
				<1 8 225>,
			<3 600000000>,
				<1 9 187>,
			<4 787200000>,
				<1 10 150>,
			<5 998400000>,
				<1 11 75>;
			<6 1094400000>,
			<7 1190400000>,
			<8 1305600000>,
			<9 1344000000>,
			<10 1401600000>,
			<11 1497600000>,
			<12 1593600000>,
			<13 1689600000>,
			<14 1785600000>;
	qcom,cpr-speed-bin-max-corners =
			<0 2 2 4 7>,
			<1 2 2 4 12>,
			<2 2 2 4 10>,
			<5 2 2 4 14>;
	qcom,cpr-quot-adjust-scaling-factor-max = <650>;
};
};


&msm_gpu {
&msm_gpu {
+22 −8
Original line number Original line Diff line number Diff line
@@ -144,15 +144,29 @@
	regulator-min-microvolt = <1>;
	regulator-min-microvolt = <1>;
	regulator-max-microvolt = <14>;
	regulator-max-microvolt = <14>;
	qcom,cpr-corner-map = <1 1 2 2 3 3 3 3 3 3 3 3 3 3>;
	qcom,cpr-corner-map = <1 1 2 2 3 3 3 3 3 3 3 3 3 3>;
	qcom,cpr-quot-adjust-table =
				<1 5 450>,
				<1 6 375>,
				<1 7 300>,
				<1 8 225>,
				<1 9 187>,
				<1 10 150>,
				<1 11 75>;
	qcom,cpr-quotient-adjustment = <0 72 72>;
	qcom,cpr-quotient-adjustment = <0 72 72>;
	qcom,pvs-version-fuse-sel = <22 4 2 0>;
	qcom,cpr-corner-frequency-map =
			<1 300000000>,
			<2 384000000>,
			<3 600000000>,
			<4 787200000>,
			<5 998400000>,
			<6 1094400000>,
			<7 1190400000>,
			<8 1305600000>,
			<9 1344000000>,
			<10 1401600000>,
			<11 1497600000>,
			<12 1593600000>,
			<13 1689600000>,
			<14 1785600000>;
	qcom,cpr-speed-bin-max-corners =
			<0 1 2 4 7>,
			<1 1 2 4 12>,
			<2 1 2 4 10>,
			<5 1 2 4 14>;
	qcom,cpr-quot-adjust-scaling-factor-max = <650>;
};
};


&tsens {
&tsens {
+187 −50
Original line number Original line Diff line number Diff line
@@ -177,6 +177,8 @@ struct cpr_regulator {
	/* Process voltage variables */
	/* Process voltage variables */
	u32		pvs_bin;
	u32		pvs_bin;
	u32		speed_bin;
	u32		speed_bin;
	u32		pvs_version;

	/* APC voltage regulator */
	/* APC voltage regulator */
	struct regulator	*vdd_apc;
	struct regulator	*vdd_apc;


@@ -1301,14 +1303,48 @@ static int cpr_voltage_uplift_wa_inc_quot(struct cpr_regulator *cpr_vreg,
	return rc;
	return rc;
}
}


static int cpr_get_of_corner_mappings(struct cpr_regulator *cpr_vreg,
static void cpr_parse_pvs_version_fuse(struct cpr_regulator *cpr_vreg,
				struct device_node *of_node)
{
	int rc;
	u64 fuse_bits;
	u32 fuse_sel[4];

	rc = of_property_read_u32_array(of_node,
			"qcom,pvs-version-fuse-sel", fuse_sel, 4);
	if (!rc) {
		fuse_bits = cpr_read_efuse_row(cpr_vreg,
				fuse_sel[0], fuse_sel[3]);
		cpr_vreg->pvs_version = (fuse_bits >> fuse_sel[1]) &
			((1 << fuse_sel[2]) - 1);
		pr_info("[row: %d]: 0x%llx, pvs_version = %d\n",
				fuse_sel[0], fuse_bits, cpr_vreg->pvs_version);
	} else {
		cpr_vreg->pvs_version = UINT_MAX;
	}
}

/*
 * cpr_get_corner_quot_adjustment() -- get the quot_adjust for each corner.
 *
 * Get the corner to fuse corner (SVS/NORMAL/TURBO) mappings and corner to
 * APC clock frequency mappings from device tree.
 * Calculate the quotient adjustment scaling factor for those corners mapping
 * to the TURBO fuse corner.
 * Calculate the quotient adjustment for each corner which map to the TURBO
 * fuse corner.
 */
static int cpr_get_corner_quot_adjustment(struct cpr_regulator *cpr_vreg,
					struct device *dev)
					struct device *dev)
{
{
	int rc = 0;
	int rc = 0;
	int i, size, stripe_size;
	int i, size;
	struct property *prop;
	struct property *prop;
	u32 *tmp;
	bool corners_mapped;
	bool corners_mapped;
	u32 *tmp, *freq_mappings = NULL;
	u32 scaling, max_factor;
	u32 corner, turbo_corner = 0, normal_corner = 0, svs_corner = 0;
	u32 freq_turbo, freq_normal, freq_corner;


	prop = of_find_property(dev->of_node, "qcom,cpr-corner-map", NULL);
	prop = of_find_property(dev->of_node, "qcom,cpr-corner-map", NULL);


@@ -1323,81 +1359,182 @@ static int cpr_get_of_corner_mappings(struct cpr_regulator *cpr_vreg,
	cpr_vreg->corner_map = devm_kzalloc(dev, sizeof(int) * (size + 1),
	cpr_vreg->corner_map = devm_kzalloc(dev, sizeof(int) * (size + 1),
					GFP_KERNEL);
					GFP_KERNEL);
	if (!cpr_vreg->corner_map) {
	if (!cpr_vreg->corner_map) {
		pr_err("Can't allocate cpr_vreg->corner_map memory\n");
		pr_err("Can't allocate memory for cpr_vreg->corner_map\n");
		return -ENOMEM;
		return -ENOMEM;
	}
	}
	cpr_vreg->num_corners = size;
	cpr_vreg->num_corners = size;


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

	if (!corners_mapped) {
	if (!corners_mapped) {
		for (i = CPR_FUSE_CORNER_SVS; i < CPR_FUSE_CORNER_MAX; i++)
		for (i = CPR_FUSE_CORNER_SVS; i < CPR_FUSE_CORNER_MAX; i++)
			cpr_vreg->corner_map[i] = i;
			cpr_vreg->corner_map[i] = i;
		return 0;
	} else {
	} else {
		rc = of_property_read_u32_array(dev->of_node,
		rc = of_property_read_u32_array(dev->of_node,
			"qcom,cpr-corner-map", &cpr_vreg->corner_map[1], size);
			"qcom,cpr-corner-map", &cpr_vreg->corner_map[1], size);


		if (rc) {
		if (rc) {
			pr_err("qcom,cpr-corner-map missing, rc = %d", rc);
			pr_err("qcom,cpr-corner-map missing, rc = %d\n", rc);
			return rc;
			return rc;
		}
		}
	}
	}


	cpr_vreg->quot_adjust = devm_kzalloc(dev,
	prop = of_find_property(dev->of_node,
			sizeof(int) * (cpr_vreg->num_corners + 1),
			"qcom,cpr-speed-bin-max-corners", NULL);
			GFP_KERNEL);
	if (!prop) {
	if (!cpr_vreg->quot_adjust) {
		cpr_debug("qcom,cpr-speed-bin-max-corner missing\n");
		pr_err("Can't allocate cpr_vreg->quot_adjust memory\n");
		return 0;
	}

	size = prop->length / sizeof(u32);
	tmp = kzalloc(size * sizeof(u32), GFP_KERNEL);
	if (!tmp) {
		pr_err("memory alloc failed\n");
		return -ENOMEM;
		return -ENOMEM;
	}
	}
	rc = of_property_read_u32_array(dev->of_node,
		"qcom,cpr-speed-bin-max-corners", tmp, size);
	if (rc < 0) {
		kfree(tmp);
		pr_err("get cpr-speed-bin-max-corners failed, rc = %d\n", rc);
		return rc;
	}


	prop = of_find_property(dev->of_node, "qcom,cpr-quot-adjust-table",
	cpr_parse_pvs_version_fuse(cpr_vreg, dev->of_node);
				NULL);


	if (prop) {
	/*
		if (!corners_mapped) {
	 * According to speed_bin && pvs_version, get the maximum
			pr_err("qcom,cpr-corner-map missing\n");
	 * corner corresponding to SVS/NORMAL/TURBO fuse corner.
			return -EINVAL;
	 */
	for (i = 0; i < size; i += 5) {
		if (tmp[i] == cpr_vreg->speed_bin &&
			tmp[i + 1] == cpr_vreg->pvs_version) {
			svs_corner = tmp[i + 2];
			normal_corner = tmp[i + 3];
			turbo_corner = tmp[i + 4];
			break;
		}
	}
	kfree(tmp);
	/*
	 * Return success if the virtual corner values read from
	 * qcom,cpr-speed-bin-max-corners property are incorrect,
	 * which make sure the driver could continue run without
	 * error.
	 */
	if (turbo_corner <= normal_corner ||
			turbo_corner > cpr_vreg->num_corners) {
		cpr_debug("turbo:%d should be larger than normal:%d\n",
				turbo_corner, normal_corner);
		return 0;
	}

	prop = of_find_property(dev->of_node,
			"qcom,cpr-corner-frequency-map", NULL);
	if (!prop) {
		cpr_debug("qcom,cpr-corner-frequency-map missing\n");
		return 0;
	}
	}


	size = prop->length / sizeof(u32);
	size = prop->length / sizeof(u32);
	tmp = kzalloc(sizeof(u32) * size, GFP_KERNEL);
	tmp = kzalloc(sizeof(u32) * size, GFP_KERNEL);
		if (!tmp)
	if (!tmp) {
		pr_err("memory alloc failed\n");
		return -ENOMEM;
		return -ENOMEM;

	}
	rc = of_property_read_u32_array(dev->of_node,
	rc = of_property_read_u32_array(dev->of_node,
				"qcom,cpr-quot-adjust-table", tmp, size);
		"qcom,cpr-corner-frequency-map", tmp, size);
		if (rc) {
	if (rc < 0) {
			pr_err("qcom,cpr-quot-adjust-table missing, rc = %d",
		pr_err("get cpr-corner-frequency-map failed, rc = %d\n", rc);
				rc);
		kfree(tmp);
		kfree(tmp);
		return rc;
		return rc;
	}
	}

	freq_mappings = kzalloc(sizeof(u32) * (cpr_vreg->num_corners + 1),
		stripe_size = sizeof(struct quot_adjust_info) / sizeof(int);
			GFP_KERNEL);

	if (!freq_mappings) {
		if ((size % stripe_size) != 0) {
		pr_err("memory alloc for freq_mappings failed!\n");
			pr_err("qcom,cpr-quot-adjust-table data is not correct");
		kfree(tmp);
		kfree(tmp);
			return -EINVAL;
		return -ENOMEM;
	}
	}

	for (i = 0; i < size; i += 2) {
		for (i = 0; i < size; i += stripe_size) {
		corner = tmp[i];
			if (tmp[i] == cpr_vreg->speed_bin) {
		if ((corner < 1) || (corner > cpr_vreg->num_corners)) {
				if (tmp[i + 1] >= 1 &&
			pr_err("corner should be in 1~%d range: %d\n",
					tmp[i + 1] <=
					cpr_vreg->num_corners, corner);
					cpr_vreg->num_corners) {
			continue;
					cpr_vreg->quot_adjust[tmp[i + 1]] =
					tmp[i + 2];
				} else {
					pr_err("qcom,cpr-quot-adjust-table data is not correct");
					kfree(tmp);
					return -EINVAL;
		}
		}
		freq_mappings[corner] = tmp[i + 1];
		cpr_debug("Frequency at virtual corner %d is %d Hz.\n",
				corner, freq_mappings[corner]);
	}
	}
	kfree(tmp);

	rc = of_property_read_u32(dev->of_node,
		"qcom,cpr-quot-adjust-scaling-factor-max",
		&max_factor);
	if (rc < 0) {
		cpr_debug("get cpr-quot-adjust-scaling-factor-max failed\n");
		kfree(freq_mappings);
		return 0;
	}
	}


		kfree(tmp);
	/*
	 * Get the quot adjust scaling factor, according to:
	 * scaling =
	 * min(1000 * (QUOT(fused @turbo) - QUOT(fused @normal)) /
	 * (freq_turbo - freq_normal), max_factor)
	 *
	 * @QUOT(fused @turbo): quotient read from fuse for TURBO fuse corner;
	 * @QUOT(fused @normal): quotient read from fuse for NORMAL fuse corner;
	 * @freq_turbo: MHz, max frequency running at TURBO fuse corner;
	 * @freq_normal: MHz, max frequency running at NORMAL fuse corner.
	 */

	freq_turbo = freq_mappings[turbo_corner];
	freq_normal = freq_mappings[normal_corner];
	if (freq_normal == 0 || freq_turbo <= freq_normal) {
		pr_err("freq_turbo: %d should larger than freq_normal: %d\n",
				freq_turbo, freq_normal);
		kfree(freq_mappings);
		return -EINVAL;
	}
	}
	freq_turbo /= 1000000;	/* MHz */
	freq_normal /= 1000000;
	scaling = 1000 *
		(cpr_vreg->cpr_fuse_target_quot[CPR_FUSE_CORNER_TURBO] -
		cpr_vreg->cpr_fuse_target_quot[CPR_FUSE_CORNER_NORMAL]) /
		(freq_turbo - freq_normal);
	scaling = min(scaling, max_factor);
	pr_info("quotient adjustment scaling factor: %d.%03d\n",
			scaling / 1000, scaling % 1000);


	/*
	 * Walk through the corners mapped to the TURBO fuse corner and
	 * calculate the quotient adjustment for each one using the following
	 * formula:
	 * quot_adjust = (freq_turbo - freq_corner) * scaling / 1000
	 *
	 * @freq_turbo: MHz, max frequency running at TURBO fuse corner;
	 * @freq_corner: MHz, frequency running at a corner.
	 */
	for (i = turbo_corner; i > normal_corner; i--) {
		freq_corner = freq_mappings[i] / 1000000; /* MHz */
		if (freq_corner > 0) {
			cpr_vreg->quot_adjust[i] =
				scaling * (freq_turbo - freq_corner) / 1000;
		}
		pr_info("adjusted quotient[%d] = %d\n", i,
			(cpr_vreg->cpr_fuse_target_quot[cpr_vreg->corner_map[i]]
				- cpr_vreg->quot_adjust[i]));
	}
	kfree(freq_mappings);
	return 0;
	return 0;
}
}


@@ -1541,7 +1678,7 @@ static int cpr_init_cpr_efuse(struct platform_device *pdev,
		}
		}
	}
	}


	rc = cpr_get_of_corner_mappings(cpr_vreg, &pdev->dev);
	rc = cpr_get_corner_quot_adjustment(cpr_vreg, &pdev->dev);
	if (rc)
	if (rc)
		return rc;
		return rc;