msm: dcvs: add frequency transient control to sysfs

It is useful to be able to toggle at runtime whether
particular DCVS points are used as transient levels. This
adds a sysfs node per core which when read returns a list
of the currently enabled DCVS transient levels. Writing
the frequency of a DCVS transient level toggles whether it
is enabled or not.

Change-Id: I69ebb6974e97efa832798047259b9acdfd08aa7c
Signed-off-by: Steve Muckle <smuckle@codeaurora.org>
(cherry picked from commit 2f6db34b3216695d4f624d2204d5e1356e62c3d5)
This commit is contained in:
Steve Muckle
2012-10-17 16:09:37 -07:00
committed by Stephen Boyd
parent 7ef421aecd
commit b3bac64cee

View File

@@ -61,6 +61,8 @@ struct core_attribs {
struct kobj_attribute thermal_poll_ms;
struct kobj_attribute freq_tbl;
struct attribute_group attrib_group;
};
@@ -633,11 +635,83 @@ DCVS_ENERGY_PARAM(leakage_coeff_d)
DCVS_PARAM_STORE(thermal_poll_ms)
static ssize_t msm_dcvs_attr_freq_tbl_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
struct msm_dcvs_freq_entry *freq_tbl;
char *buf_idx = buf;
int i, len;
struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, freq_tbl);
freq_tbl = core->info->freq_tbl;
*buf_idx = '\0';
/* limit the number of frequencies we will print into
* the PAGE_SIZE sysfs show buffer. */
if (core->info->power_param.num_freq > 64)
return 0;
for (i = 0; i < core->info->power_param.num_freq; i++) {
if (freq_tbl[i].is_trans_level) {
len = snprintf(buf_idx, 10, "%7d ", freq_tbl[i].freq);
/* buf_idx always points at terminating null */
buf_idx += len;
}
}
/* overwrite final trailing space with newline */
if (buf_idx > buf)
*(buf_idx - 1) = '\n';
return buf_idx - buf;
}
static ssize_t msm_dcvs_attr_freq_tbl_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf,
size_t count)
{
struct msm_dcvs_freq_entry *freq_tbl;
uint32_t freq;
int i, ret;
struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, freq_tbl);
freq_tbl = core->info->freq_tbl;
ret = kstrtouint(buf, 10, &freq);
if (ret) {
__err("Invalid input %s for freq_tbl\n", buf);
return count;
}
for (i = 0; i < core->info->power_param.num_freq; i++)
if (freq_tbl[i].freq == freq) {
freq_tbl[i].is_trans_level ^= 1;
break;
}
if (i >= core->info->power_param.num_freq) {
__err("Invalid frequency for freq_tbl: %d\n", freq);
return count;
}
ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
&core->info->power_param,
&core->info->freq_tbl[0],
&core->coeffs);
if (ret) {
freq_tbl[i].is_trans_level ^= 1;
__err("Error %d in toggling freq %d (orig enable val %d)\n",
ret, freq_tbl[i].freq, freq_tbl[i].is_trans_level);
}
return count;
}
static int msm_dcvs_setup_core_sysfs(struct dcvs_core *core)
{
int ret = 0;
struct kobject *core_kobj = NULL;
const int attr_count = 24;
const int attr_count = 25;
BUG_ON(!cores_kobj);
@@ -675,7 +749,9 @@ static int msm_dcvs_setup_core_sysfs(struct dcvs_core *core)
DCVS_RW_ATTRIB(21, leakage_coeff_d);
DCVS_RW_ATTRIB(22, thermal_poll_ms);
core->attrib.attrib_group.attrs[23] = NULL;
DCVS_RW_ATTRIB(23, freq_tbl);
core->attrib.attrib_group.attrs[24] = NULL;
core_kobj = kobject_create_and_add(core->core_name, cores_kobj);
if (!core_kobj) {