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

This commit is contained in:
Linux Build Service Account 2016-10-06 19:45:58 -07:00 committed by Gerrit - the friendly Code Review server
commit b1dbfd04c7
4 changed files with 201 additions and 83 deletions

View file

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

View file

@ -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__ */

View file

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

View file

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