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

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

Merge "regulator: cprh-kbss-regulator: add CPR aging adjustment support"

parents 787b63ec 8402e718
Loading
Loading
Loading
Loading
+6 −4
Original line number Diff line number Diff line
@@ -3691,14 +3691,16 @@ static void cpr3_regulator_readjust_volt_and_quot(struct cpr3_regulator *vreg,
static void cpr3_regulator_set_aging_ref_adjustment(
		struct cpr3_controller *ctrl, int ref_adjust_volt)
{
	struct cpr3_regulator *vreg;
	int i, j;

	for (i = 0; i < ctrl->thread_count; i++) {
		for (j = 0; j < ctrl->thread[i].vreg_count; j++) {
			cpr3_regulator_readjust_volt_and_quot(
				&ctrl->thread[i].vreg[j],
				ctrl->aging_ref_adjust_volt,
				ref_adjust_volt);
			vreg = &ctrl->thread[i].vreg[j];
			cpr3_regulator_readjust_volt_and_quot(vreg,
				ctrl->aging_ref_adjust_volt, ref_adjust_volt);
			if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPRH)
				cprh_adjust_voltages_for_apm(vreg);
		}
	}

+5 −0
Original line number Diff line number Diff line
@@ -875,6 +875,7 @@ int cpr4_parse_core_count_temp_voltage_adj(struct cpr3_regulator *vreg,
			bool use_corner_band);
int cpr3_apm_init(struct cpr3_controller *ctrl);
int cpr3_mem_acc_init(struct cpr3_regulator *vreg);
void cprh_adjust_voltages_for_apm(struct cpr3_regulator *vreg);

#else

@@ -1047,6 +1048,10 @@ static inline int cpr3_mem_acc_init(struct cpr3_regulator *vreg)
	return 0;
}

static inline void cprh_adjust_voltages_for_apm(struct cpr3_regulator *vreg)
{
}

#endif /* CONFIG_REGULATOR_CPR3 */

#endif /* __REGULATOR_CPR_REGULATOR_H__ */
+75 −0
Original line number Diff line number Diff line
@@ -2008,3 +2008,78 @@ done:

	return rc;
}

/**
 * cprh_adjust_voltages_for_apm() - adjust per-corner floor and ceiling voltages
 *		so that they do not overlap the APM threshold voltage.
 * @vreg:		Pointer to the CPR3 regulator
 *
 * The memory array power mux (APM) must be configured for a specific supply
 * based upon where the VDD voltage lies with respect to the APM threshold
 * voltage.  When using CPR hardware closed-loop, the voltage may vary anywhere
 * between the floor and ceiling voltage without software notification.
 * Therefore, it is required that the floor to ceiling range for every corner
 * not intersect the APM threshold voltage.  This function adjusts the floor to
 * ceiling range for each corner which violates this requirement.
 *
 * The following algorithm is applied:
 *	if floor < threshold <= ceiling:
 *		if open_loop >= threshold, then floor = threshold - adj
 *		else ceiling = threshold - step
 * where:
 *	adj = APM hysteresis voltage established to minimize the number of
 *	      corners with artificially increased floor voltages
 *	step = voltage in microvolts of a single step of the VDD supply
 *
 * The open-loop voltage is also bounded by the new floor or ceiling value as
 * needed.
 *
 * Return: none
 */
void cprh_adjust_voltages_for_apm(struct cpr3_regulator *vreg)
{
	struct cpr3_controller *ctrl = vreg->thread->ctrl;
	struct cpr3_corner *corner;
	int i, adj, threshold, prev_ceiling, prev_floor, prev_open_loop;

	if (!ctrl->apm_threshold_volt) {
		/* APM not being used. */
		return;
	}

	ctrl->apm_threshold_volt = CPR3_ROUND(ctrl->apm_threshold_volt,
						ctrl->step_volt);
	ctrl->apm_adj_volt = CPR3_ROUND(ctrl->apm_adj_volt, ctrl->step_volt);

	threshold = ctrl->apm_threshold_volt;
	adj = ctrl->apm_adj_volt;

	for (i = 0; i < vreg->corner_count; i++) {
		corner = &vreg->corner[i];

		if (threshold <= corner->floor_volt
		    || threshold > corner->ceiling_volt)
			continue;

		prev_floor = corner->floor_volt;
		prev_ceiling = corner->ceiling_volt;
		prev_open_loop = corner->open_loop_volt;

		if (corner->open_loop_volt >= threshold) {
			corner->floor_volt = max(corner->floor_volt,
						 threshold - adj);
			if (corner->open_loop_volt < corner->floor_volt)
				corner->open_loop_volt = corner->floor_volt;
		} else {
			corner->ceiling_volt = threshold - ctrl->step_volt;
		}

		if (corner->floor_volt != prev_floor
		    || corner->ceiling_volt != prev_ceiling
		    || corner->open_loop_volt != prev_open_loop)
			cpr3_debug(vreg, "APM threshold=%d, APM adj=%d changed corner %d voltages; prev: floor=%d, ceiling=%d, open-loop=%d; new: floor=%d, ceiling=%d, open-loop=%d\n",
				threshold, adj, i, prev_floor, prev_ceiling,
				prev_open_loop, corner->floor_volt,
				corner->ceiling_volt, corner->open_loop_volt);
	}
}
+115 −79
Original line number Diff line number Diff line
@@ -54,6 +54,8 @@
 * @force_highest_corner:	Flag indicating that all corners must operate
 *			at the voltage of the highest corner.  This is
 *			applicable to MSMCOBALT only.
 * @aging_init_quot_diff:	Initial quotient difference between CPR aging
 *			min and max sensors measured at time of manufacturing
 *
 * This struct holds the values for all of the fuses read from memory.
 */
@@ -65,6 +67,7 @@ struct cprh_msmcobalt_kbss_fuses {
	u64	speed_bin;
	u64	cpr_fusing_rev;
	u64	force_highest_corner;
	u64	aging_init_quot_diff;
};

/*
@@ -192,6 +195,18 @@ msmcobalt_cpr_force_highest_corner_param[] = {
	{},
};

static const struct cpr3_fuse_param
msmcobalt_kbss_aging_init_quot_diff_param[2][2] = {
	[MSMCOBALT_KBSS_POWER_CLUSTER_ID] = {
		{69, 6, 13},
		{},
	},
	[MSMCOBALT_KBSS_PERFORMANCE_CLUSTER_ID] = {
		{71, 25, 32},
		{},
	},
};

/*
 * Open loop voltage fuse reference voltages in microvolts for MSMCOBALT v1
 */
@@ -225,6 +240,8 @@ msmcobalt_v2_kbss_fuse_ref_volt[2][MSMCOBALT_KBSS_FUSE_CORNERS] = {
#define MSMCOBALT_KBSS_FUSE_STEP_VOLT		10000
#define MSMCOBALT_KBSS_VOLTAGE_FUSE_SIZE	6
#define MSMCOBALT_KBSS_QUOT_OFFSET_SCALE	5
#define MSMCOBALT_KBSS_AGING_INIT_QUOT_DIFF_SIZE	8
#define MSMCOBALT_KBSS_AGING_INIT_QUOT_DIFF_SCALE	1

#define MSMCOBALT_KBSS_POWER_CPR_SENSOR_COUNT	6
#define MSMCOBALT_KBSS_PERFORMANCE_CPR_SENSOR_COUNT	9
@@ -242,6 +259,12 @@ msmcobalt_v2_kbss_fuse_ref_volt[2][MSMCOBALT_KBSS_FUSE_CORNERS] = {
#define MSMCOBALT_KBSS_PERFORMANCE_TEMP_SENSOR_ID_START	6
#define MSMCOBALT_KBSS_PERFORMANCE_TEMP_SENSOR_ID_END	11

#define MSMCOBALT_KBSS_POWER_AGING_SENSOR_ID		0
#define MSMCOBALT_KBSS_POWER_AGING_BYPASS_MASK0		0

#define MSMCOBALT_KBSS_PERFORMANCE_AGING_SENSOR_ID	0
#define MSMCOBALT_KBSS_PERFORMANCE_AGING_BYPASS_MASK0	0

/**
 * cprh_msmcobalt_kbss_read_fuse_data() - load KBSS specific fuse parameter values
 * @vreg:		Pointer to the CPR3 regulator
@@ -320,6 +343,15 @@ static int cprh_msmcobalt_kbss_read_fuse_data(struct cpr3_regulator *vreg)

	}

	rc = cpr3_read_fuse_param(base,
				msmcobalt_kbss_aging_init_quot_diff_param[id],
				&fuse->aging_init_quot_diff);
	if (rc) {
		cpr3_err(vreg, "Unable to read aging initial quotient difference fuse, rc=%d\n",
			rc);
		return rc;
	}

	rc = cpr3_read_fuse_param(base,
		  msmcobalt_cpr_force_highest_corner_param,
		  &fuse->force_highest_corner);
@@ -826,85 +858,13 @@ static int cprh_kbss_apm_crossover_as_corner(struct cpr3_regulator *vreg)
	corner->floor_volt = ctrl->apm_crossover_volt;
	corner->ceiling_volt = ctrl->apm_crossover_volt;
	corner->open_loop_volt = ctrl->apm_crossover_volt;
	corner->abs_ceiling_volt = ctrl->apm_crossover_volt;
	corner->use_open_loop = true;
	vreg->corner_count++;

	return 0;
}

/**
 * cprh_kbss_adjust_voltages_for_apm() - adjust per-corner floor and ceiling
 *		voltages so that they do not overlap the APM threshold voltage.
 * @vreg:		Pointer to the CPR3 regulator
 *
 * The KBSS memory array power mux (APM) must be configured for a specific
 * supply based upon where the VDD voltage lies with respect to the APM
 * threshold voltage.  When using CPR hardware closed-loop, the voltage may vary
 * anywhere between the floor and ceiling voltage without software notification.
 * Therefore, it is required that the floor to ceiling range for every corner
 * not intersect the APM threshold voltage.  This function adjusts the floor to
 * ceiling range for each corner which violates this requirement.
 *
 * The following algorithm is applied in the case that
 * floor < threshold <= ceiling:
 *	if open_loop >= threshold, then floor = threshold - adj
 *	else ceiling = threshold - step
 * where adj = APM hysteresis voltage established to minimize number
 * of corners with artificially increased floor voltages
 * and step = voltage in microvolts of a single step of the VDD supply
 *
 * The open-loop voltage is also bounded by the new floor or ceiling value as
 * needed.
 *
 * Return: 0 on success, errno on failure
 */
static int cprh_kbss_adjust_voltages_for_apm(struct cpr3_regulator *vreg)
{
	struct cpr3_controller *ctrl = vreg->thread->ctrl;
	struct cpr3_corner *corner;
	int i, adj, threshold, prev_ceiling, prev_floor, prev_open_loop;

	if (!ctrl->apm_threshold_volt) {
		/* APM not being used. */
		return 0;
	}

	ctrl->apm_threshold_volt = CPR3_ROUND(ctrl->apm_threshold_volt,
						ctrl->step_volt);
	ctrl->apm_adj_volt = CPR3_ROUND(ctrl->apm_adj_volt, ctrl->step_volt);

	threshold = ctrl->apm_threshold_volt;
	adj = ctrl->apm_adj_volt;

	for (i = 0; i < vreg->corner_count; i++) {
		corner = &vreg->corner[i];

		if (threshold <= corner->floor_volt
		    || threshold > corner->ceiling_volt)
			continue;

		prev_floor = corner->floor_volt;
		prev_ceiling = corner->ceiling_volt;
		prev_open_loop = corner->open_loop_volt;

		if (corner->open_loop_volt >= threshold) {
			corner->floor_volt = max(corner->floor_volt,
						 threshold - adj);
			if (corner->open_loop_volt < corner->floor_volt)
				corner->open_loop_volt = corner->floor_volt;
		} else {
			corner->ceiling_volt = threshold - ctrl->step_volt;
		}

		cpr3_debug(vreg, "APM threshold=%d, APM adj=%d changed corner %d voltages; prev: floor=%d, ceiling=%d, open-loop=%d; new: floor=%d, ceiling=%d, open-loop=%d\n",
			threshold, adj, i, prev_floor, prev_ceiling,
			prev_open_loop, corner->floor_volt,
			corner->ceiling_volt, corner->open_loop_volt);
	}

	return 0;
}

/**
 * cprh_msmcobalt_kbss_set_no_interpolation_quotients() - use the fused target
 *		quotient values for lower frequencies.
@@ -1235,12 +1195,7 @@ static int cprh_kbss_init_regulator(struct cpr3_regulator *vreg)
		return rc;
	}

	rc = cprh_kbss_adjust_voltages_for_apm(vreg);
	if (rc) {
		cpr3_err(vreg, "unable to adjust voltages for APM\n, rc=%d\n",
			rc);
		return rc;
	}
	cprh_adjust_voltages_for_apm(vreg);

	cpr3_open_loop_voltage_as_ceiling(vreg);

@@ -1298,6 +1253,80 @@ static int cprh_kbss_init_regulator(struct cpr3_regulator *vreg)
	return 0;
}

/**
 * cprh_kbss_init_aging() - perform KBSS CPRh controller specific aging
 *		initializations
 * @ctrl:		Pointer to the CPR3 controller
 *
 * Return: 0 on success, errno on failure
 */
static int cprh_kbss_init_aging(struct cpr3_controller *ctrl)
{
	struct cprh_msmcobalt_kbss_fuses *fuse = NULL;
	struct cpr3_regulator *vreg;
	u32 aging_ro_scale;
	int i, j, rc;

	for (i = 0; i < ctrl->thread_count; i++) {
		for (j = 0; j < ctrl->thread[i].vreg_count; j++) {
			if (ctrl->thread[i].vreg[j].aging_allowed) {
				ctrl->aging_required = true;
				vreg = &ctrl->thread[i].vreg[j];
				fuse = vreg->platform_fuses;
				break;
			}
		}
	}

	if (!ctrl->aging_required || !fuse || !vreg)
		return 0;

	rc = cpr3_parse_array_property(vreg, "qcom,cpr-aging-ro-scaling-factor",
					1, &aging_ro_scale);
	if (rc)
		return rc;

	if (aging_ro_scale == 0) {
		cpr3_err(ctrl, "aging RO scaling factor is invalid: %u\n",
			aging_ro_scale);
		return -EINVAL;
	}

	ctrl->aging_vdd_mode = REGULATOR_MODE_NORMAL;
	ctrl->aging_complete_vdd_mode = REGULATOR_MODE_IDLE;

	ctrl->aging_sensor_count = 1;
	ctrl->aging_sensor = kzalloc(sizeof(*ctrl->aging_sensor), GFP_KERNEL);
	if (!ctrl->aging_sensor)
		return -ENOMEM;

	if (ctrl->ctrl_id == MSMCOBALT_KBSS_POWER_CLUSTER_ID) {
		ctrl->aging_sensor->sensor_id
			= MSMCOBALT_KBSS_POWER_AGING_SENSOR_ID;
		ctrl->aging_sensor->bypass_mask[0]
			= MSMCOBALT_KBSS_POWER_AGING_BYPASS_MASK0;
	} else  {
		ctrl->aging_sensor->sensor_id
			= MSMCOBALT_KBSS_PERFORMANCE_AGING_SENSOR_ID;
		ctrl->aging_sensor->bypass_mask[0]
			= MSMCOBALT_KBSS_PERFORMANCE_AGING_BYPASS_MASK0;
	}
	ctrl->aging_sensor->ro_scale = aging_ro_scale;

	ctrl->aging_sensor->init_quot_diff
		= cpr3_convert_open_loop_voltage_fuse(0,
			MSMCOBALT_KBSS_AGING_INIT_QUOT_DIFF_SCALE,
			fuse->aging_init_quot_diff,
			MSMCOBALT_KBSS_AGING_INIT_QUOT_DIFF_SIZE);

	cpr3_debug(ctrl, "sensor %u aging init quotient diff = %d, aging RO scale = %u QUOT/V\n",
		ctrl->aging_sensor->sensor_id,
		ctrl->aging_sensor->init_quot_diff,
		ctrl->aging_sensor->ro_scale);

	return 0;
}

/**
 * cprh_kbss_init_controller() - perform KBSS CPRh controller specific
 *		initializations
@@ -1566,6 +1595,13 @@ static int cprh_kbss_regulator_probe(struct platform_device *pdev)
		return rc;
	}

	rc = cprh_kbss_init_aging(ctrl);
	if (rc) {
		cpr3_err(ctrl, "failed to initialize aging configurations, rc=%d\n",
			rc);
		return rc;
	}

	platform_set_drvdata(pdev, ctrl);

	rc = cprh_kbss_populate_opp_table(ctrl);