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

Commit a9563290 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: cpr-regulator: calculate quotient adjustment dynamically"

parents 2c8f23f4 3659eb76
Loading
Loading
Loading
Loading
+56 −19
Original line number 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
				pvs voltage uplift workaround.
				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.
				The 3 quotient adjustments with index[0..2] are:
				[0] => amount to add to the SVS quotient
@@ -230,6 +215,42 @@ Optional properties:
				Not Present: No such regulator.
- vdd-apc-optional-sec-supply:	Present: Regulator of second highest priority to supply VDD APC power.
				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:
	apc_vreg_corner: regulator@f9018000 {
@@ -319,9 +340,25 @@ Example:
		qcom,cpr-uplift-speed-bin = <1>;
		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-quot-adjust-table = <1 1 0>, <1 2 0>, <1 3 0>,
						<1 4 0>, <1 5 450>, <1 6 375>,
						<1 7 300>, <1 8 225>, <1 9 187>,
						<1 10 150>, <1 11 75>, <1 12 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>;
		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 Diff line number Diff line
@@ -37,14 +37,28 @@
	qcom,cpr-up-threshold = <0>;
	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-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,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 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 {
+22 −8
Original line number Diff line number Diff line
@@ -144,15 +144,29 @@
	regulator-min-microvolt = <1>;
	regulator-max-microvolt = <14>;
	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,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 {
+187 −50
Original line number Diff line number Diff line
@@ -177,6 +177,8 @@ struct cpr_regulator {
	/* Process voltage variables */
	u32		pvs_bin;
	u32		speed_bin;
	u32		pvs_version;

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

@@ -1301,14 +1303,48 @@ static int cpr_voltage_uplift_wa_inc_quot(struct cpr_regulator *cpr_vreg,
	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)
{
	int rc = 0;
	int i, size, stripe_size;
	int i, size;
	struct property *prop;
	u32 *tmp;
	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);

@@ -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),
					GFP_KERNEL);
	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;
	}
	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) {
		for (i = CPR_FUSE_CORNER_SVS; i < CPR_FUSE_CORNER_MAX; i++)
			cpr_vreg->corner_map[i] = i;
		return 0;
	} else {
		rc = of_property_read_u32_array(dev->of_node,
			"qcom,cpr-corner-map", &cpr_vreg->corner_map[1], size);

		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;
		}
	}

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

	if (prop) {
		if (!corners_mapped) {
			pr_err("qcom,cpr-corner-map missing\n");
			return -EINVAL;
	/*
	 * According to speed_bin && pvs_version, get the maximum
	 * corner corresponding to SVS/NORMAL/TURBO fuse corner.
	 */
	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);
	tmp = kzalloc(sizeof(u32) * size, GFP_KERNEL);
		if (!tmp)
	if (!tmp) {
		pr_err("memory alloc failed\n");
		return -ENOMEM;

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

		stripe_size = sizeof(struct quot_adjust_info) / sizeof(int);

		if ((size % stripe_size) != 0) {
			pr_err("qcom,cpr-quot-adjust-table data is not correct");
	freq_mappings = kzalloc(sizeof(u32) * (cpr_vreg->num_corners + 1),
			GFP_KERNEL);
	if (!freq_mappings) {
		pr_err("memory alloc for freq_mappings failed!\n");
		kfree(tmp);
			return -EINVAL;
		return -ENOMEM;
	}

		for (i = 0; i < size; i += stripe_size) {
			if (tmp[i] == cpr_vreg->speed_bin) {
				if (tmp[i + 1] >= 1 &&
					tmp[i + 1] <=
					cpr_vreg->num_corners) {
					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;
	for (i = 0; i < size; i += 2) {
		corner = tmp[i];
		if ((corner < 1) || (corner > cpr_vreg->num_corners)) {
			pr_err("corner should be in 1~%d range: %d\n",
					cpr_vreg->num_corners, corner);
			continue;
		}
		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;
}

@@ -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)
		return rc;