From b3bac64ceeda453eacfd95f1c4dabeb6abfeec25 Mon Sep 17 00:00:00 2001 From: Steve Muckle Date: Wed, 17 Oct 2012 16:09:37 -0700 Subject: [PATCH] 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 (cherry picked from commit 2f6db34b3216695d4f624d2204d5e1356e62c3d5) --- arch/arm/mach-msm/msm_dcvs.c | 80 +++++++++++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/msm_dcvs.c b/arch/arm/mach-msm/msm_dcvs.c index 310197ed9a7..b2160c518c4 100644 --- a/arch/arm/mach-msm/msm_dcvs.c +++ b/arch/arm/mach-msm/msm_dcvs.c @@ -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) {