diff --git a/kernel/arch/arm/configs/uckernel_defconfig b/kernel/arch/arm/configs/uckernel_defconfig index 287349e5..1200ab3d 100644 --- a/kernel/arch/arm/configs/uckernel_defconfig +++ b/kernel/arch/arm/configs/uckernel_defconfig @@ -484,6 +484,7 @@ CONFIG_CPU_FREQ_GOV_INTERACTIVEX=y CONFIG_CPU_FREQ_GOV_SAVAGEDZEN=y CONFIG_CPU_FREQ_GOV_SMARTASS2=y CONFIG_CPU_FREQ_GOV_SMOOTHASS=y +CONFIG_CPU_FREQ_VDD_LEVELS=y CONFIG_CPU_FREQ_MIN_TICKS=10 CONFIG_CPU_FREQ_SAMPLING_LATENCY_MULTIPLIER=1000 CONFIG_CPU_IDLE=y diff --git a/kernel/arch/arm/mach-omap2/voltage.c b/kernel/arch/arm/mach-omap2/voltage.c index 639bec74..52bdacfc 100755 --- a/kernel/arch/arm/mach-omap2/voltage.c +++ b/kernel/arch/arm/mach-omap2/voltage.c @@ -506,6 +506,96 @@ static int vp_debug_get(void *data, u64 *val) return 0; } + +int volt_debug_set(void *data, unsigned long val, int indx) +{ + struct omap_vdd_info *vdd = container_of((struct voltagedomain *) data, struct omap_vdd_info, voltdm); + struct omap_volt_data *vdata; + int res = 1; + + int i,j; + u64 old_val; + struct device *mpu_dev = omap2_get_mpuss_device(); + struct cpufreq_frequency_table *mpu_freq_table = *omap_pm_cpu_get_freq_table(); + struct omap_opp *dsp_opp_table = omap_pm_dsp_get_opp_table(); + struct omap_opp *temp_opp; + + if (!vdd) { + pr_warning("Wrong paramater passed\n"); + return -EINVAL; + } + + res = omap_vscale_pause(&vdd->voltdm,false); + if(res == 0) { + /*vdata = omap_voltage_get_nom_volt(&vdd->voltdm);*/ + vdata = vdd->volt_data; + if (IS_ERR_OR_NULL(vdata)) + return -EINVAL; + + printk(KERN_ERR "%s: Requested change from %d to/with %lu \n", __func__,vdata[indx].volt_nominal,val); + + if(indx != -1) + { + for (i = 0; i < vdd->dev_count; i++) { + opp_set_voltage(vdd->dev_list[i], vdata[indx].volt_nominal, val, true); + } + old_val = vdata[indx].volt_nominal; + vdata[indx].volt_nominal = val; + } + else + { + for (j = 0; j < vdd->volt_data_count; j++) { + for (i = 0; i < vdd->dev_count; i++) { + opp_set_voltage(vdd->dev_list[i], vdata[j].volt_nominal, vdata[j].volt_nominal+val, true); + } + old_val = vdata[j].volt_nominal; + vdata[j].volt_nominal = vdata[j].volt_nominal + val; + } + } + //omap_voltage_scale(&vdd->voltdm); + omap_voltage_reset(&vdd->voltdm); + + if(!mpu_dev || !mpu_freq_table) + { + if (mpu_freq_table == NULL) + printk(KERN_ERR "%s: Could not get freq_table\n", __func__); + return -EINVAL; + } + + if(indx != -1) + { + i = indx; + temp_opp = opp_find_freq_exact(mpu_dev, mpu_freq_table[i].frequency*1000, true); + if(IS_ERR(temp_opp)) + return -EINVAL; + opp_disable(temp_opp); + temp_opp->u_volt = val; + opp_enable(temp_opp); + + opp_disable(&dsp_opp_table[i]); + dsp_opp_table[i].u_volt = val; + opp_enable(&dsp_opp_table[i]); + } + else + for (i = 0; mpu_freq_table[i].frequency != CPUFREQ_TABLE_END; i++) + { + temp_opp = opp_find_freq_exact(mpu_dev, mpu_freq_table[i].frequency*1000, true); + if(IS_ERR(temp_opp)) + return -EINVAL; + opp_disable(temp_opp); + temp_opp->u_volt = temp_opp->u_volt + val; + opp_enable(temp_opp); + + opp_disable(&dsp_opp_table[i]); + dsp_opp_table[i].u_volt = dsp_opp_table[i].u_volt + val; + opp_enable(&dsp_opp_table[i]); + } + + omap_vscale_unpause(&vdd->voltdm); + } + return 0; +} + static int vp_debug_set(void *data, u64 val) { if (enable_sr_vp_debug) { @@ -2578,8 +2668,8 @@ int omap_voltage_scale(struct voltagedomain *voltdm) opp = opp_find_voltage(vdd->dev_list[i], volt, true); if (IS_ERR(opp)) { - dev_err(vdd->dev_list[i], "%s: Unable to find OPP for" - "volt%ld\n", __func__, volt); + dev_err(vdd->dev_list[i], "%s: Unable to find OPP for " + "volt %ld\n", __func__, volt); continue; } diff --git a/kernel/arch/arm/plat-omap/include/plat/opp.h b/kernel/arch/arm/plat-omap/include/plat/opp.h index 4a3359bf..1643a683 100755 --- a/kernel/arch/arm/plat-omap/include/plat/opp.h +++ b/kernel/arch/arm/plat-omap/include/plat/opp.h @@ -98,6 +98,9 @@ struct omap_opp *opp_find_freq_ceil(struct device *dev, unsigned long *freq); struct omap_opp *opp_find_voltage(struct device *dev, unsigned long volt, bool enabled); +struct omap_opp *opp_set_voltage(struct device *dev, unsigned long volt, unsigned long new_volt, + bool enabled); + int opp_set_rate(struct device *dev, unsigned long freq); unsigned long opp_get_rate(struct device *dev); @@ -164,6 +167,13 @@ static inline struct omap_opp *opp_find_voltage(struct device *dev, return ERR_PTR(-EINVAL); } +static inline struct omap_opp *opp_set_voltage(struct device *dev, + unsigned long volt, + bool enabled) +{ + return ERR_PTR(-EINVAL); +} + static inline int opp_set_rate(struct device *dev, unsigned long freq) { return -EINVAL; diff --git a/kernel/arch/arm/plat-omap/include/plat/voltage.h b/kernel/arch/arm/plat-omap/include/plat/voltage.h index 5e55deae..ff6cc00d 100755 --- a/kernel/arch/arm/plat-omap/include/plat/voltage.h +++ b/kernel/arch/arm/plat-omap/include/plat/voltage.h @@ -140,6 +140,7 @@ bool omap_vp_clear_transdone(struct voltagedomain *voltdm); int omap_voltage_calib_reset(struct voltagedomain *voltdm); int omap_vscale_pause(struct voltagedomain *voltdm, bool trylock); int omap_vscale_unpause(struct voltagedomain *voltdm); +int volt_debug_set(void *vdd, unsigned long val, int indx); #ifdef CONFIG_PM void omap_voltage_init_vc(struct omap_volt_vc_data *setup_vc); diff --git a/kernel/arch/arm/plat-omap/opp.c b/kernel/arch/arm/plat-omap/opp.c index e226212f..04b3edeb 100755 --- a/kernel/arch/arm/plat-omap/opp.c +++ b/kernel/arch/arm/plat-omap/opp.c @@ -366,6 +366,41 @@ struct omap_opp *opp_find_voltage(struct device *dev, unsigned long volt, return opp; } +/** + * opp_set_voltage() - Modify an exact voltage + * @dev: device pointer associated with the opp type + * @volt: voltage to replace for + * @new_volt: New voltage value + * + * Replaces an exact match in the opp list and returns handle to the matching + * opp if found, else returns ERR_PTR in case of error and should be handled + * using IS_ERR. + * + * Note enabled is a modifier for the search. If enabled is true then the + * matching opp must be enabled. If enabled is false then the matching opp + * must be disabled. + */ +struct omap_opp *opp_set_voltage(struct device *dev, unsigned long volt, unsigned long new_volt, + bool enabled) +{ + struct device_opp *dev_opp; + struct omap_opp *temp_opp, *opp = ERR_PTR(-ENODEV); + + dev_opp = find_device_opp(dev); + if (IS_ERR(dev_opp)) + return opp; + + list_for_each_entry(temp_opp, &dev_opp->opp_list, node) { + if (!(temp_opp->enabled ^ enabled) && + temp_opp->u_volt == volt) { + opp = temp_opp; + break; + } + } + opp->u_volt = new_volt; + return opp; +} + /** * opp_set_rate() - Change the operating frequency of the device * @dev: device pointer associated with the opp type diff --git a/kernel/drivers/cpufreq/Kconfig b/kernel/drivers/cpufreq/Kconfig index 6936fb40..b5559f85 100755 --- a/kernel/drivers/cpufreq/Kconfig +++ b/kernel/drivers/cpufreq/Kconfig @@ -418,4 +418,11 @@ config CPU_FREQ_GOV_HOTPLUG If in doubt, say N. +config CPU_FREQ_VDD_LEVELS + bool "CPU Vdd levels sysfs interface" + depends on CPU_FREQ_STAT + default n + help + CPU Vdd levels sysfs interface + endif # CPU_FREQ diff --git a/kernel/drivers/cpufreq/cpufreq.c b/kernel/drivers/cpufreq/cpufreq.c index 6a60ac52..780b046d 100755 --- a/kernel/drivers/cpufreq/cpufreq.c +++ b/kernel/drivers/cpufreq/cpufreq.c @@ -28,6 +28,11 @@ #include #include #include +#include +#include +#include +#include +#include #define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_CORE, \ "cpufreq-core", msg) @@ -691,6 +696,157 @@ static ssize_t show_bios_limit(struct cpufreq_policy *policy, char *buf) return sprintf(buf, "%u\n", policy->cpuinfo.max_freq); } + +#ifdef CONFIG_CPU_FREQ_VDD_LEVELS + +//extern ssize_t acpuclk_get_vdd_levels_str(char *buf); +static ssize_t show_vdd_levels(struct cpufreq_policy *policy, char *buf) +{ + int len = 0, i; + unsigned long *vdd = -1; + unsigned long *temp_vdd = -1; + char *voltdm_name = "mpu"; + struct device *mpu_dev = omap2_get_mpuss_device(); + struct cpufreq_frequency_table *mpu_freq_table = *omap_pm_cpu_get_freq_table(); + struct omap_opp *temp_opp; + struct voltagedomain *mpu_voltdm; + struct omap_volt_data *mpu_voltdata; + + if(!mpu_dev || !mpu_freq_table) + { + if (mpu_freq_table == NULL) + printk(KERN_ERR "%s: Could not get freq_table\n", __func__); + return -EINVAL; + } + + if (buf) + { + mpu_voltdm = omap_voltage_domain_get(voltdm_name); + for (i = 0; mpu_freq_table[i].frequency != CPUFREQ_TABLE_END; i++) + { + temp_opp = opp_find_freq_exact(mpu_dev, mpu_freq_table[i].frequency*1000, true); + if(IS_ERR(temp_opp)) + return -EINVAL; + temp_vdd = opp_get_voltage(temp_opp); + mpu_voltdata = omap_voltage_get_voltdata(mpu_voltdm, temp_vdd); + vdd = mpu_voltdata->volt_nominal; + len += sprintf(buf + len, "%lu: %lu\n", mpu_freq_table[i].frequency/1000, vdd); + } + + } + return len; +} + +extern void acpuclk_set_vdd(unsigned acpu_khz, int vdd); +static ssize_t store_vdd_levels(struct cpufreq_policy *policy, const char *buf, size_t count) +{ + int len = 0, i = 0; + unsigned long *vdd = -1; + unsigned long *temp_vdd = -1; + char *voltdm_name = "mpu"; + struct device *mpu_dev = omap2_get_mpuss_device(); + struct cpufreq_frequency_table *mpu_freq_table = *omap_pm_cpu_get_freq_table(); + struct omap_opp *temp_opp; + struct voltagedomain *mpu_voltdm; + struct omap_volt_data *mpu_voltdata; + + printk(KERN_ERR "%s: Buf:\t%s\n", __func__,buf); + + if(!mpu_dev || !mpu_freq_table) + { + if (mpu_freq_table == NULL) + printk(KERN_ERR "%s: Could not get freq_table\n", __func__); + return -EINVAL; + } + + if (buf) + { + int j; + int pair[2] = { 0, 0 }; + int sign = 0; + unsigned int return_Freq = 0; + unsigned long return_Vdd = 0; + + if (count < 1) + return 0; + + if (buf[0] == '-') + { + sign = -1; + i++; + } + else if (buf[0] == '+') + { + sign = 1; + i++; + } + + for (j = 0; i < count; i++) + { + char c = buf[i]; + if ((c >= '0') && (c <= '9')) + { + pair[j] *= 10; + pair[j] += (c - '0'); + } + else if ((c == ' ') || (c == '\t')) + { + if (pair[j] != 0) + { + j++; + if ((sign != 0) || (j > 1)) + break; + } + } + else + break; + } + + if (sign != 0) + { + if (pair[0] > 0) + { + return_Freq = 0; + return_Vdd = sign * pair[0]; + } + } + else + { + if ((pair[0] > 0) && (pair[1] > 0)) + { + return_Freq = (unsigned)pair[0]; + return_Vdd = pair[1]; + } + else + { + printk(KERN_ERR "%s: Do not provide 0 as input\nInput entries:\t%d,\t%d\n and input buffer:%s", __func__,pair[0],pair[1],buf); + return -EINVAL; + } + } + + mpu_voltdm = omap_voltage_domain_get(voltdm_name); + + printk(KERN_ERR "%s: return_Freq:%d\treturn_Vdd:%lu\n", __func__,return_Freq,return_Vdd); + + for (i = 0; mpu_freq_table[i].frequency != CPUFREQ_TABLE_END; i++) + { + if(return_Freq != 0 && return_Freq == mpu_freq_table[i].frequency) + volt_debug_set(mpu_voltdm, return_Vdd, i); + else if(return_Freq == 0) + { + volt_debug_set(mpu_voltdm, return_Vdd, -1); + break; + } + else + continue; + } + } + return count; +} + +#endif + + cpufreq_freq_attr_ro_perm(cpuinfo_cur_freq, 0400); cpufreq_freq_attr_ro(cpuinfo_min_freq); cpufreq_freq_attr_ro(cpuinfo_max_freq); @@ -707,6 +863,11 @@ cpufreq_freq_attr_rw(scaling_governor); cpufreq_freq_attr_rw(scaling_setspeed); cpufreq_freq_attr_rw(boost_cpufreq); +#ifdef CONFIG_CPU_FREQ_VDD_LEVELS + cpufreq_freq_attr_rw(vdd_levels); +#endif + + static struct attribute *default_attrs[] = { &cpuinfo_min_freq.attr, &cpuinfo_max_freq.attr, @@ -720,6 +881,11 @@ static struct attribute *default_attrs[] = { &scaling_available_governors.attr, &scaling_setspeed.attr, &boost_cpufreq.attr, + +#ifdef CONFIG_CPU_FREQ_VDD_LEVELS + &vdd_levels.attr, +#endif + NULL };