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

Commit 26263a33 authored by Wu Fenglin's avatar Wu Fenglin Committed by Gerrit - the friendly Code Review server
Browse files

regulator: cpr-regulator: add support for per corner initial voltage fuses



A new initial voltage coding scheme has been introduced which defines
the initial voltage for each fuse corner using its own fuse value.
Previously, the initial voltage for all fuse corners was determined
from a single PVS bin fuse value. Add support in the cpr-regulator
driver for this new initial voltage coding scheme.

Each fuse encodes the initial voltage for a fuse corner as follows. The
MSB of the fuse value is a sign bit. The remaining bits define an offset.
The offset has units of a predetermined step size which is captured in
device tree. The initial voltage is then: ceiling_voltage - sign *
offset.

Change-Id: I6d48f3c68d62adc730fd406f937f7f3033b73552
Signed-off-by: default avatarWu Fenglin <fenglinw@codeaurora.org>
parent a1806c18
Loading
Loading
Loading
Loading
+57 −32
Original line number Original line Diff line number Diff line
@@ -22,10 +22,6 @@ Required properties:
				should be 1 for SVS corner
				should be 1 for SVS corner
- regulator-max-microvolt:	Maximum corner value as max constraint, which
- regulator-max-microvolt:	Maximum corner value as max constraint, which
				should be 4 for SUPER_TURBO or 3 for TURBO
				should be 4 for SUPER_TURBO or 3 for TURBO
- qcom,pvs-voltage-table: 	Array of triples in which each triple indicates the initial voltage
				of the PVS bin in SVS, NOM and Turbo corner in microvolts.
				The location or 0-based index of an triple in the
				list corresponds to the bin number.
- qcom,cpr-voltage-ceiling:	Ceiling voltages of SVS, NOM and TURBO corners respectively
- qcom,cpr-voltage-ceiling:	Ceiling voltages of SVS, NOM and TURBO corners respectively
- qcom,cpr-voltage-floor:	Floor voltages of SVS, NOM and TURBO corners respectively
- qcom,cpr-voltage-floor:	Floor voltages of SVS, NOM and TURBO corners respectively
				The ceiling voltages for each of above two
				The ceiling voltages for each of above two
@@ -52,33 +48,6 @@ Required properties:
- qcom,cpr-idle-clocks:		Idle clock cycles RO can be in.
- qcom,cpr-idle-clocks:		Idle clock cycles RO can be in.
- qcom,cpr-gcnt-time:		The time for gate count in microseconds.
- qcom,cpr-gcnt-time:		The time for gate count in microseconds.
- qcom,cpr-apc-volt-step:	The voltage in microvolt per CPR step, such as 5000uV.
- qcom,cpr-apc-volt-step:	The voltage in microvolt per CPR step, such as 5000uV.

- qcom,pvs-fuse-redun-sel:	Array of 5 elements to indicate where to read the bits, what value to
				compare with in order to decide if the redundant PVS fuse bits would be
				used instead of the original bits and method to read fuse row, reading
				register through SCM or directly. The 5 elements with index [0..4] are:
				  [0] => the fuse row number of the selector
				  [1] => LSB bit position of the bits
				  [2] => number of bits
				  [3] => the value to indicate redundant selection
				  [4] => fuse reading method, 0 for direct reading or 1 for SCM reading
				When the value of the fuse bits specified by first 3 elements equals to
				the value in 4th element, redundant PVS fuse bits should be selected.
				Otherwise, the original PVS bits should be selected. If the 5th
				element is 0, read the fuse row from register directly. Otherwise,
				read it through SCM.
- qcom,pvs-fuse:			Array of 4 elements to indicate the bits for PVS fuse and read method.
				The array should have index and value like this:
				  [0] => the PVS fuse row number
				  [1] => LSB bit position of the bits
				  [2] => number of bits
				  [3] => fuse reading method, 0 for direct reading or 1 for SCM reading
- qcom,pvs-fuse-redun:		Array of 4 elements to indicate the bits for redundant PVS fuse.
				The array should have index and value like this:
				  [0] => the redundant PVS fuse row number
				  [1] => LSB bit position of the bits
				  [2] => number of bits
				  [3] => fuse reading method, 0 for direct reading or 1 for SCM reading
- qcom,cpr-fuse-redun-sel:	Array of 5 elements to indicate where to read the bits, what value to
- qcom,cpr-fuse-redun-sel:	Array of 5 elements to indicate where to read the bits, what value to
				compare with in order to decide if the redundant CPR fuse bits would be
				compare with in order to decide if the redundant CPR fuse bits would be
				used instead of the original bits and method to read fuse row, using SCM
				used instead of the original bits and method to read fuse row, using SCM
@@ -146,6 +115,41 @@ Optional properties:
				[1] => voltage to set for vdd-mx when VDD_APC is running at NOM corner
				[1] => voltage to set for vdd-mx when VDD_APC is running at NOM corner
				[2] => voltage to set for vdd-mx when VDD_APC is running at TURBO corner
				[2] => voltage to set for vdd-mx when VDD_APC is running at TURBO corner
				This is required when the qcom,vdd-mx-vmin-method property has a value of 4.
				This is required when the qcom,vdd-mx-vmin-method property has a value of 4.
- qcom,pvs-voltage-table: 	Array of triples in which each triple indicates the initial voltage
				of the PVS bin in SVS, NOM and Turbo corner in microvolts.
				The location or 0-based index of an triple in the
				list corresponds to the bin number.
				A given cpr-regulator device must have either qcom,pvs-voltage-table specified
				or qcom,cpr-fuse-init-voltage (and its associated properties).
- qcom,pvs-fuse-redun-sel:	Array of 5 elements to indicate where to read the bits, what value to
				compare with in order to decide if the redundant PVS fuse bits would be
				used instead of the original bits and method to read fuse row, reading
				register through SCM or directly. The 5 elements with index [0..4] are:
				  [0] => the fuse row number of the selector
				  [1] => LSB bit position of the bits
				  [2] => number of bits
				  [3] => the value to indicate redundant selection
				  [4] => fuse reading method, 0 for direct reading or 1 for SCM reading
				When the value of the fuse bits specified by first 3 elements equals to
				the value in 4th element, redundant PVS fuse bits should be selected.
				Otherwise, the original PVS bits should be selected. If the 5th
				element is 0, read the fuse row from register directly. Otherwise,
				read it through SCM.
				This property is required if qcom,pvs-voltage-table is present.
- qcom,pvs-fuse:		Array of 4 elements to indicate the bits for PVS fuse and read method.
				The array should have index and value like this:
				  [0] => the PVS fuse row number
				  [1] => LSB bit position of the bits
				  [2] => number of bits
				  [3] => fuse reading method, 0 for direct reading or 1 for SCM reading
				This property is required if qcom,pvs-voltage-table is present.
- qcom,pvs-fuse-redun:		Array of 4 elements to indicate the bits for redundant PVS fuse.
				The array should have index and value like this:
				  [0] => the redundant PVS fuse row number
				  [1] => LSB bit position of the bits
				  [2] => number of bits
				  [3] => fuse reading method, 0 for direct reading or 1 for SCM reading
				This property is required if qcom,pvs-voltage-table is present.
- qcom,cpr-fuse-redun-bp-cpr-disable:	Redundant bit position of the bit to indicate if CPR should be disable
- qcom,cpr-fuse-redun-bp-cpr-disable:	Redundant bit position of the bit to indicate if CPR should be disable
- qcom,cpr-fuse-redun-bp-scheme:	Redundant bit position of the bit to indicate if it's a global/local scheme
- qcom,cpr-fuse-redun-bp-scheme:	Redundant bit position of the bit to indicate if it's a global/local scheme
					This property is required if cpr-fuse-redun-bp-cpr-disable
					This property is required if cpr-fuse-redun-bp-cpr-disable
@@ -251,7 +255,23 @@ Optional properties:
				corresponds to 'scaling' in this equation:
				corresponds to 'scaling' in this equation:
				quot_adjust = (freq_turbo - freq_corner) * scaling / 1000.
				quot_adjust = (freq_turbo - freq_corner) * scaling / 1000.
				This property is required if qcom,cpr-speed-bin-max-corners is present.
				This property is required if qcom,cpr-speed-bin-max-corners is present.

- qcom,cpr-fuse-init-voltage:	Array of quadruples in which each quadruple specifies a fuse location to
				read in order to get an initial voltage for the SVS, NORMAL, or TURBO fuse
				corner. The fuse values are encoded as voltage steps higher or lower than
				the voltages defined in qcom,cpr-voltage-ceiling. Each step corresponds
				to the voltage defined by the qcom,cpr-init-voltage-step property.
				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 voltage fuse corner to the
				highest voltage fuse corner.
				A given cpr-regulator device must have either qcom,cpr-fuse-init-voltage
				specified or qcom,pvs-voltage-table (and its associated properties).
- qcom,cpr-init-voltage-step:	The voltage step size in microvolts of the CPR initial voltage fuses described by the
				qcom,cpr-fuse-init-voltage property.
				This property is required if qcom,cpr-fuse-init-voltage is present.
Example:
Example:
	apc_vreg_corner: regulator@f9018000 {
	apc_vreg_corner: regulator@f9018000 {
		status = "okay";
		status = "okay";
@@ -360,5 +380,10 @@ Example:
				<2 1 2 4 10>,
				<2 1 2 4 10>,
				<5 1 2 4 14>;
				<5 1 2 4 14>;
		qcom,cpr-quot-adjust-scaling-factor-max = <650>;
		qcom,cpr-quot-adjust-scaling-factor-max = <650>;
		qcom,cpr-fuse-init-voltage =
				<27 36 6 0>,
				<27 18 6 0>,
				<27 0 6 0>;
		qcom,cpr-init-voltage-step = <10000>;
	};
	};
+136 −17
Original line number Original line Diff line number Diff line
@@ -1093,13 +1093,100 @@ static int cpr_voltage_uplift_wa_inc_volt(struct cpr_regulator *cpr_vreg,
	return rc;
	return rc;
}
}


static int cpr_pvs_init(struct platform_device *pdev,
/*
 * Property qcom,cpr-fuse-init-voltage specifies the fuse position of the
 * initial voltage for each fuse corner. MSB of the fuse value is a sign
 * bit, and the remaining bits define the steps of the offset. Each step has
 * units of microvolts defined in the qcom,cpr-fuse-init-voltage-step property.
 * The initial voltages can be calculated using the formula:
 * pvs_corner_v[corner] = ceiling_volt[corner] + (sign * steps * step_size_uv)
 */
static int cpr_pvs_per_corner_init(struct device_node *of_node,
				struct cpr_regulator *cpr_vreg)
{
	u64 efuse_bits;
	int i, size, sign, steps, step_size_uv, rc;
	u32 *fuse_sel, *tmp;
	struct property *prop;

	prop = of_find_property(of_node, "qcom,cpr-fuse-init-voltage", NULL);
	if (!prop) {
		pr_err("qcom,cpr-fuse-init-voltage is missing\n");
		return -EINVAL;
	}
	size = prop->length / sizeof(u32);
	if (size != (CPR_FUSE_CORNER_MAX - 1) * 4) {
		pr_err("fuse position for init voltages is invalid\n");
		return -EINVAL;
	}
	fuse_sel = kzalloc(sizeof(u32) * size, GFP_KERNEL);
	if (!fuse_sel) {
		pr_err("memory alloc failed.\n");
		return -ENOMEM;
	}
	rc = of_property_read_u32_array(of_node, "qcom,cpr-fuse-init-voltage",
							fuse_sel, size);
	if (rc < 0) {
		pr_err("read cpr-fuse-init-voltage failed, rc = %d\n", rc);
		kfree(fuse_sel);
		return rc;
	}
	rc = of_property_read_u32(of_node, "qcom,cpr-init-voltage-step",
							&step_size_uv);
	if (rc < 0) {
		pr_err("read cpr-init-voltage-step failed, rc = %d\n", rc);
		kfree(fuse_sel);
		return rc;
	}
	tmp = fuse_sel;
	for (i = CPR_FUSE_CORNER_SVS; i < CPR_FUSE_CORNER_MAX; i++) {
		efuse_bits = cpr_read_efuse_row(cpr_vreg, fuse_sel[0],
							fuse_sel[3]);
		sign = (efuse_bits >> fuse_sel[1]) & (1 << (fuse_sel[2] - 1));
		sign = ((sign == 0) ? 1 : -1);
		steps = (efuse_bits >> fuse_sel[1]) &
			((1 << (fuse_sel[2] - 1)) - 1);
		pr_debug("corner %d: sign = %d, steps = %d\n", i, sign, steps);
		cpr_vreg->pvs_corner_v[i] = cpr_vreg->ceiling_volt[i] +
					sign * steps * step_size_uv;
		cpr_vreg->pvs_corner_v[i] = DIV_ROUND_UP(
				cpr_vreg->pvs_corner_v[i],
				cpr_vreg->step_volt) *
				cpr_vreg->step_volt;
		if (cpr_vreg->pvs_corner_v[i] > cpr_vreg->ceiling_volt[i]) {
			pr_info("Warning: initial voltage[%d] %d above ceiling %d\n",
						i, cpr_vreg->pvs_corner_v[i],
						cpr_vreg->ceiling_volt[i]);
			cpr_vreg->pvs_corner_v[i] = cpr_vreg->ceiling_volt[i];
		} else if (cpr_vreg->pvs_corner_v[i] <
				cpr_vreg->floor_volt[i]) {
			pr_info("Warning: initial voltage[%d] %d below floor %d\n",
						i, cpr_vreg->pvs_corner_v[i],
						cpr_vreg->floor_volt[i]);
			cpr_vreg->pvs_corner_v[i] = cpr_vreg->floor_volt[i];
		}
		fuse_sel += 4;
	}
	kfree(tmp);

	return 0;
}

/*
 * A single PVS bin is stored in a fuse that's position is defined either
 * in the qcom,pvs-fuse-redun property or in the qcom,pvs-fuse property.
 * The fuse value defined in the qcom,pvs-fuse-redun-sel property is used
 * to pick between the primary or redudant PVS fuse position.
 * After the PVS bin value is read out successfully, it is used as the row
 * index to get initial voltages for each fuse corner from the voltage table
 * defined in the qcom,pvs-voltage-table property.
 */
static int cpr_pvs_single_bin_init(struct device_node *of_node,
				struct cpr_regulator *cpr_vreg)
				struct cpr_regulator *cpr_vreg)
{
{
	struct device_node *of_node = pdev->dev.of_node;
	u64 efuse_bits;
	u64 efuse_bits;
	int rc, i, stripe_size;
	u32 pvs_fuse[4], pvs_fuse_redun_sel[5];
	u32 pvs_fuse[4], pvs_fuse_redun_sel[5];
	int rc, i, stripe_size;
	bool redundant;
	bool redundant;
	size_t pvs_bins;
	size_t pvs_bins;
	u32 *tmp;
	u32 *tmp;
@@ -1112,7 +1199,6 @@ static int cpr_pvs_init(struct platform_device *pdev,
	}
	}


	redundant = cpr_fuse_is_setting_expected(cpr_vreg, pvs_fuse_redun_sel);
	redundant = cpr_fuse_is_setting_expected(cpr_vreg, pvs_fuse_redun_sel);

	if (redundant) {
	if (redundant) {
		rc = of_property_read_u32_array(of_node, "qcom,pvs-fuse-redun",
		rc = of_property_read_u32_array(of_node, "qcom,pvs-fuse-redun",
								pvs_fuse, 4);
								pvs_fuse, 4);
@@ -1130,13 +1216,10 @@ static int cpr_pvs_init(struct platform_device *pdev,
	}
	}


	/* Construct PVS process # from the efuse bits */
	/* Construct PVS process # from the efuse bits */

	efuse_bits = cpr_read_efuse_row(cpr_vreg, pvs_fuse[0], pvs_fuse[3]);
	efuse_bits = cpr_read_efuse_row(cpr_vreg, pvs_fuse[0], pvs_fuse[3]);
	cpr_vreg->pvs_bin = (efuse_bits >> pvs_fuse[1]) &
	cpr_vreg->pvs_bin = (efuse_bits >> pvs_fuse[1]) &
				((1 << pvs_fuse[2]) - 1);
				((1 << pvs_fuse[2]) - 1);

	pvs_bins = 1 << pvs_fuse[2];
	pvs_bins = 1 << pvs_fuse[2];

	stripe_size = CPR_FUSE_CORNER_MAX - 1;
	stripe_size = CPR_FUSE_CORNER_MAX - 1;
	tmp = kzalloc(sizeof(u32) * pvs_bins * stripe_size, GFP_KERNEL);
	tmp = kzalloc(sizeof(u32) * pvs_bins * stripe_size, GFP_KERNEL);
	if (!tmp) {
	if (!tmp) {
@@ -1157,6 +1240,46 @@ static int cpr_pvs_init(struct platform_device *pdev,
						stripe_size + i - 1];
						stripe_size + i - 1];
	kfree(tmp);
	kfree(tmp);


	return 0;
}

/*
 * The initial voltage for each fuse corner may be determined by one of two
 * possible styles of fuse. If qcom,cpr-fuse-init-voltage is present, then
 * the initial voltages are encoded in a fuse for each fuse corner. If it is
 * not present, then the initial voltages are all determined using a single
 * PVS bin fuse value.
 */
static int cpr_pvs_init(struct platform_device *pdev,
			       struct cpr_regulator *cpr_vreg)
{
	struct device_node *of_node = pdev->dev.of_node;
	int i, rc;

	rc = of_property_read_u32(of_node, "qcom,cpr-apc-volt-step",
					&cpr_vreg->step_volt);
	if (rc < 0) {
		pr_err("read cpr-apc-volt-step failed, rc = %d\n", rc);
		return rc;
	} else if (cpr_vreg->step_volt == 0) {
		pr_err("apc voltage step size can't be set to 0.\n");
		return -EINVAL;
	}

	if (of_find_property(of_node, "qcom,cpr-fuse-init-voltage", NULL)) {
		rc = cpr_pvs_per_corner_init(of_node, cpr_vreg);
		if (rc < 0) {
			pr_err("get pvs per corner failed, rc = %d", rc);
			return rc;
		}
	} else {
		rc = cpr_pvs_single_bin_init(of_node, cpr_vreg);
		if (rc < 0) {
			pr_err("get pvs from single bin failed, rc = %d", rc);
			return rc;
		}
	}

	if (cpr_vreg->flags & FLAGS_UPLIFT_QUOT_VOLT) {
	if (cpr_vreg->flags & FLAGS_UPLIFT_QUOT_VOLT) {
		rc = cpr_voltage_uplift_wa_inc_volt(cpr_vreg, of_node);
		rc = cpr_voltage_uplift_wa_inc_volt(cpr_vreg, of_node);
		if (rc < 0) {
		if (rc < 0) {
@@ -1784,10 +1907,6 @@ static int cpr_init_cpr_parameters(struct platform_device *pdev,
			  &cpr_vreg->vdd_apc_step_down_limit, rc);
			  &cpr_vreg->vdd_apc_step_down_limit, rc);
	if (rc)
	if (rc)
		return rc;
		return rc;
	CPR_PROP_READ_U32(of_node, "cpr-apc-volt-step",
			  &cpr_vreg->step_volt, rc);
	if (rc)
		return rc;


	/* Init module parameter with the DT value */
	/* Init module parameter with the DT value */
	cpr_vreg->enable = of_property_read_bool(of_node, "qcom,cpr-enable");
	cpr_vreg->enable = of_property_read_bool(of_node, "qcom,cpr-enable");