in qcom/cpr.c [1076:1225]
static int cpr_corner_init(struct cpr_drv *drv)
{
const struct cpr_desc *desc = drv->desc;
const struct cpr_fuse *fuses = drv->cpr_fuses;
int i, level, scaling = 0;
unsigned int fnum, fc;
const char *quot_offset;
struct fuse_corner *fuse, *prev_fuse;
struct corner *corner, *end;
struct corner_data *cdata;
const struct fuse_corner_data *fdata;
bool apply_scaling;
unsigned long freq_diff, freq_diff_mhz;
unsigned long freq;
int step_volt = regulator_get_linear_step(drv->vdd_apc);
struct dev_pm_opp *opp;
if (!step_volt)
return -EINVAL;
corner = drv->corners;
end = &corner[drv->num_corners - 1];
cdata = devm_kcalloc(drv->dev, drv->num_corners,
sizeof(struct corner_data),
GFP_KERNEL);
if (!cdata)
return -ENOMEM;
/*
* Store maximum frequency for each fuse corner based on the frequency
* plan
*/
for (level = 1; level <= drv->num_corners; level++) {
opp = dev_pm_opp_find_level_exact(&drv->pd.dev, level);
if (IS_ERR(opp))
return -EINVAL;
fc = cpr_get_fuse_corner(opp);
if (!fc) {
dev_pm_opp_put(opp);
return -EINVAL;
}
fnum = fc - 1;
freq = cpr_get_opp_hz_for_req(opp, drv->attached_cpu_dev);
if (!freq) {
dev_pm_opp_put(opp);
return -EINVAL;
}
cdata[level - 1].fuse_corner = fnum;
cdata[level - 1].freq = freq;
fuse = &drv->fuse_corners[fnum];
dev_dbg(drv->dev, "freq: %lu level: %u fuse level: %u\n",
freq, dev_pm_opp_get_level(opp) - 1, fnum);
if (freq > fuse->max_freq)
fuse->max_freq = freq;
dev_pm_opp_put(opp);
}
/*
* Get the quotient adjustment scaling factor, according to:
*
* scaling = min(1000 * (QUOT(corner_N) - QUOT(corner_N-1))
* / (freq(corner_N) - freq(corner_N-1)), max_factor)
*
* QUOT(corner_N): quotient read from fuse for fuse corner N
* QUOT(corner_N-1): quotient read from fuse for fuse corner (N - 1)
* freq(corner_N): max frequency in MHz supported by fuse corner N
* freq(corner_N-1): max frequency in MHz supported by fuse corner
* (N - 1)
*
* Then walk through the corners mapped to each fuse corner
* and calculate the quotient adjustment for each one using the
* following formula:
*
* quot_adjust = (freq_max - freq_corner) * scaling / 1000
*
* freq_max: max frequency in MHz supported by the fuse corner
* freq_corner: frequency in MHz corresponding to the corner
* scaling: calculated from above equation
*
*
* + +
* | v |
* q | f c o | f c
* u | c l | c
* o | f t | f
* t | c a | c
* | c f g | c f
* | e |
* +--------------- +----------------
* 0 1 2 3 4 5 6 0 1 2 3 4 5 6
* corner corner
*
* c = corner
* f = fuse corner
*
*/
for (apply_scaling = false, i = 0; corner <= end; corner++, i++) {
fnum = cdata[i].fuse_corner;
fdata = &desc->cpr_fuses.fuse_corner_data[fnum];
quot_offset = fuses[fnum].quotient_offset;
fuse = &drv->fuse_corners[fnum];
if (fnum)
prev_fuse = &drv->fuse_corners[fnum - 1];
else
prev_fuse = NULL;
corner->fuse_corner = fuse;
corner->freq = cdata[i].freq;
corner->uV = fuse->uV;
if (prev_fuse && cdata[i - 1].freq == prev_fuse->max_freq) {
scaling = cpr_calculate_scaling(quot_offset, drv,
fdata, corner);
if (scaling < 0)
return scaling;
apply_scaling = true;
} else if (corner->freq == fuse->max_freq) {
/* This is a fuse corner; don't scale anything */
apply_scaling = false;
}
if (apply_scaling) {
freq_diff = fuse->max_freq - corner->freq;
freq_diff_mhz = freq_diff / 1000000;
corner->quot_adjust = scaling * freq_diff_mhz / 1000;
corner->uV = cpr_interpolate(corner, step_volt, fdata);
}
corner->max_uV = fuse->max_uV;
corner->min_uV = fuse->min_uV;
corner->uV = clamp(corner->uV, corner->min_uV, corner->max_uV);
corner->last_uV = corner->uV;
/* Reduce the ceiling voltage if needed */
if (desc->reduce_to_corner_uV && corner->uV < corner->max_uV)
corner->max_uV = corner->uV;
else if (desc->reduce_to_fuse_uV && fuse->uV < corner->max_uV)
corner->max_uV = max(corner->min_uV, fuse->uV);
dev_dbg(drv->dev, "corner %d: [%d %d %d] quot %d\n", i,
corner->min_uV, corner->uV, corner->max_uV,
fuse->quot - corner->quot_adjust);
}
return 0;
}