Merge "regulator: cprh-kbss-regulator: add CPR aging adjustment support"
This commit is contained in:
commit
b1dbfd04c7
4 changed files with 201 additions and 83 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue