From 1c6a560b99f943219bf2f7745b7caa25c2dd0953 Mon Sep 17 00:00:00 2001 From: Per Fransson Date: Thu, 17 Mar 2011 13:46:12 +0100 Subject: ARM: ux500: Make it possible to force DDR and APE OPP Forces the OPPs to the values written, regardless of the QoS requirements. echo 25 > /debugfs/prcmu/ddr_opp echo 50 > /debugfs/prcmu/ddr_opp echo 100 > /debugfs/prcmu/ddr_opp echo 50 > /debugfs/prcmu/ape_opp echo 100 > /debugfs/prcmu/ape_opp ST-Ericsson ID: CR 323730 Change-Id: I5fd18e254f3da5342a17cd207b0ee94da0a8cc28 Signed-off-by: Per Fransson Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/18702 Reviewed-by: Martin PERSSON --- arch/arm/mach-ux500/include/mach/prcmu-fw-api.h | 3 ++ arch/arm/mach-ux500/prcmu-debug.c | 40 +++++++++++++++++++++++++ arch/arm/mach-ux500/prcmu-qos-power.c | 40 ++++++++++++++++++------- 3 files changed, 72 insertions(+), 11 deletions(-) diff --git a/arch/arm/mach-ux500/include/mach/prcmu-fw-api.h b/arch/arm/mach-ux500/include/mach/prcmu-fw-api.h index 6785b9dff6e..d4c964fa2d9 100644 --- a/arch/arm/mach-ux500/include/mach/prcmu-fw-api.h +++ b/arch/arm/mach-ux500/include/mach/prcmu-fw-api.h @@ -257,6 +257,7 @@ int prcmu_set_ddr_opp(u8 opp); int prcmu_get_ddr_opp(void); unsigned long prcmu_qos_get_cpufreq_opp_delay(void); void prcmu_qos_set_cpufreq_opp_delay(unsigned long); +void prcmu_qos_force_opp(int, s32); /* NOTE! Use regulator framework instead */ int prcmu_set_hwacc(u16 hw_acc_dev, u8 state); int prcmu_set_epod(u16 epod_id, u8 epod_state); @@ -369,6 +370,8 @@ static inline unsigned long prcmu_qos_get_cpufreq_opp_delay(void) static inline void prcmu_qos_set_cpufreq_opp_delay(unsigned long n) {} +void prcmu_qos_force_opp(int prcmu_qos_class, s32 i) {} + static inline int prcmu_set_hwacc(u16 hw_acc_dev, u8 state) { return 0; diff --git a/arch/arm/mach-ux500/prcmu-debug.c b/arch/arm/mach-ux500/prcmu-debug.c index 942740b4a23..42892d59fb1 100644 --- a/arch/arm/mach-ux500/prcmu-debug.c +++ b/arch/arm/mach-ux500/prcmu-debug.c @@ -280,6 +280,44 @@ static int ddr_opp_read(struct seq_file *s, void *p) "unknown", opp); } +static ssize_t opp_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos, int prcmu_qos_class) +{ + char buf[32]; + ssize_t buf_size; + long unsigned int i; + + /* Get userspace string and assure termination */ + buf_size = min(count, (sizeof(buf)-1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + if (strict_strtoul(buf, 0, &i) != 0) + return buf_size; + + prcmu_qos_force_opp(prcmu_qos_class, i); + + pr_info("prcmu debug: forced OPP for %d to %d\n", prcmu_qos_class, i); + + return buf_size; +} + +static ssize_t ddr_opp_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + return opp_write(file, user_buf, count, ppos, PRCMU_QOS_DDR_OPP); +} + +static ssize_t ape_opp_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + return opp_write(file, user_buf, count, ppos, PRCMU_QOS_APE_OPP); +} + static int cpufreq_delay_read(struct seq_file *s, void *p) { return seq_printf(s, "%lu\n", prcmu_qos_get_cpufreq_opp_delay()); @@ -351,6 +389,7 @@ static const struct file_operations arm_opp_fops = { static const struct file_operations ape_opp_fops = { .open = ape_opp_open_file, + .write = ape_opp_write, .read = seq_read, .llseek = seq_lseek, .release = single_release, @@ -359,6 +398,7 @@ static const struct file_operations ape_opp_fops = { static const struct file_operations ddr_opp_fops = { .open = ddr_opp_open_file, + .write = ddr_opp_write, .read = seq_read, .llseek = seq_lseek, .release = single_release, diff --git a/arch/arm/mach-ux500/prcmu-qos-power.c b/arch/arm/mach-ux500/prcmu-qos-power.c index df20e67cf67..c7fa4160f62 100644 --- a/arch/arm/mach-ux500/prcmu-qos-power.c +++ b/arch/arm/mach-ux500/prcmu-qos-power.c @@ -55,6 +55,7 @@ struct prcmu_qos_object { struct miscdevice prcmu_qos_power_miscdev; char *name; s32 default_value; + s32 force_value; atomic_t target_value; s32 (*comparitor)(s32, s32); }; @@ -71,6 +72,7 @@ static struct prcmu_qos_object ape_opp_qos = { .name = "ape_opp", /* Target value in % APE OPP */ .default_value = 50, + .force_value = 0, .target_value = ATOMIC_INIT(0), .comparitor = max_compare }; @@ -83,6 +85,7 @@ static struct prcmu_qos_object ddr_opp_qos = { .name = "ddr_opp", /* Target value in % DDR OPP */ .default_value = 25, + .force_value = 0, .target_value = ATOMIC_INIT(0), .comparitor = max_compare }; @@ -140,18 +143,27 @@ static void update_target(int target) spin_lock_irqsave(&prcmu_qos_lock, flags); extreme_value = prcmu_qos_array[target]->default_value; - list_for_each_entry(node, - &prcmu_qos_array[target]->requirements.list, list) { - extreme_value = prcmu_qos_array[target]->comparitor( - extreme_value, node->value); - } - if (atomic_read(&prcmu_qos_array[target]->target_value) - != extreme_value) { + + if (prcmu_qos_array[target]->force_value != 0) { + extreme_value = prcmu_qos_array[target]->force_value; update = 1; - atomic_set(&prcmu_qos_array[target]->target_value, - extreme_value); - pr_debug("prcmu qos: new target for qos %d is %d\n", target, - atomic_read(&prcmu_qos_array[target]->target_value)); + } else { + list_for_each_entry(node, + &prcmu_qos_array[target]->requirements.list, + list) { + extreme_value = prcmu_qos_array[target]->comparitor( + extreme_value, node->value); + } + if (atomic_read(&prcmu_qos_array[target]->target_value) + != extreme_value) { + update = 1; + atomic_set(&prcmu_qos_array[target]->target_value, + extreme_value); + pr_debug("prcmu qos: new target for qos %d is %d\n", + target, atomic_read( + &prcmu_qos_array[target]->target_value + )); + } } spin_unlock_irqrestore(&prcmu_qos_lock, flags); @@ -201,6 +213,12 @@ static void update_target(int target) } } +void prcmu_qos_force_opp(int prcmu_qos_class, s32 i) +{ + prcmu_qos_array[prcmu_qos_class]->force_value = i; + update_target(prcmu_qos_class); +} + /** * prcmu_qos_requirement - returns current prcmu qos expectation * @prcmu_qos_class: identification of which qos value is requested -- cgit v1.2.3